ni_atmio16d.c revision 2c146810e30f7818e95a90e36725c23facef833a
1/*
2   comedi/drivers/ni_atmio16d.c
3   Hardware driver for National Instruments AT-MIO16D board
4   Copyright (C) 2000 Chris R. Baugher <baugher@enteract.com>
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 */
21/*
22Driver: ni_atmio16d
23Description: National Instruments AT-MIO-16D
24Author: Chris R. Baugher <baugher@enteract.com>
25Status: unknown
26Devices: [National Instruments] AT-MIO-16 (atmio16), AT-MIO-16D (atmio16d)
27*/
28/*
29 * I must give credit here to Michal Dobes <dobes@tesnet.cz> who
30 * wrote the driver for Advantec's pcl812 boards. I used the interrupt
31 * handling code from his driver as an example for this one.
32 *
33 * Chris Baugher
34 * 5/1/2000
35 *
36 */
37
38#include <linux/interrupt.h>
39#include "../comedidev.h"
40
41#include <linux/ioport.h>
42
43#include "8255.h"
44
45/* Configuration and Status Registers */
46#define COM_REG_1	0x00	/* wo 16 */
47#define STAT_REG	0x00	/* ro 16 */
48#define COM_REG_2	0x02	/* wo 16 */
49/* Event Strobe Registers */
50#define START_CONVERT_REG	0x08	/* wo 16 */
51#define START_DAQ_REG		0x0A	/* wo 16 */
52#define AD_CLEAR_REG		0x0C	/* wo 16 */
53#define EXT_STROBE_REG		0x0E	/* wo 16 */
54/* Analog Output Registers */
55#define DAC0_REG		0x10	/* wo 16 */
56#define DAC1_REG		0x12	/* wo 16 */
57#define INT2CLR_REG		0x14	/* wo 16 */
58/* Analog Input Registers */
59#define MUX_CNTR_REG		0x04	/* wo 16 */
60#define MUX_GAIN_REG		0x06	/* wo 16 */
61#define AD_FIFO_REG		0x16	/* ro 16 */
62#define DMA_TC_INT_CLR_REG	0x16	/* wo 16 */
63/* AM9513A Counter/Timer Registers */
64#define AM9513A_DATA_REG	0x18	/* rw 16 */
65#define AM9513A_COM_REG		0x1A	/* wo 16 */
66#define AM9513A_STAT_REG	0x1A	/* ro 16 */
67/* MIO-16 Digital I/O Registers */
68#define MIO_16_DIG_IN_REG	0x1C	/* ro 16 */
69#define MIO_16_DIG_OUT_REG	0x1C	/* wo 16 */
70/* RTSI Switch Registers */
71#define RTSI_SW_SHIFT_REG	0x1E	/* wo 8 */
72#define RTSI_SW_STROBE_REG	0x1F	/* wo 8 */
73/* DIO-24 Registers */
74#define DIO_24_PORTA_REG	0x00	/* rw 8 */
75#define DIO_24_PORTB_REG	0x01	/* rw 8 */
76#define DIO_24_PORTC_REG	0x02	/* rw 8 */
77#define DIO_24_CNFG_REG		0x03	/* wo 8 */
78
79/* Command Register bits */
80#define COMREG1_2SCADC		0x0001
81#define COMREG1_1632CNT		0x0002
82#define COMREG1_SCANEN		0x0008
83#define COMREG1_DAQEN		0x0010
84#define COMREG1_DMAEN		0x0020
85#define COMREG1_CONVINTEN	0x0080
86#define COMREG2_SCN2		0x0010
87#define COMREG2_INTEN		0x0080
88#define COMREG2_DOUTEN0		0x0100
89#define COMREG2_DOUTEN1		0x0200
90/* Status Register bits */
91#define STAT_AD_OVERRUN		0x0100
92#define STAT_AD_OVERFLOW	0x0200
93#define STAT_AD_DAQPROG		0x0800
94#define STAT_AD_CONVAVAIL	0x2000
95#define STAT_AD_DAQSTOPINT	0x4000
96/* AM9513A Counter/Timer defines */
97#define CLOCK_1_MHZ		0x8B25
98#define CLOCK_100_KHZ	0x8C25
99#define CLOCK_10_KHZ	0x8D25
100#define CLOCK_1_KHZ		0x8E25
101#define CLOCK_100_HZ	0x8F25
102/* Other miscellaneous defines */
103#define ATMIO16D_SIZE	32	/* bus address range */
104#define devpriv ((struct atmio16d_private *)dev->private)
105#define ATMIO16D_TIMEOUT 10
106
107struct atmio16_board_t {
108
109	const char *name;
110	int has_8255;
111};
112
113static const struct atmio16_board_t atmio16_boards[] = {
114	{
115	 .name = "atmio16",
116	 .has_8255 = 0,
117	 },
118	{
119	 .name = "atmio16d",
120	 .has_8255 = 1,
121	 },
122};
123
124#define n_atmio16_boards ARRAY_SIZE(atmio16_boards)
125
126#define boardtype ((const struct atmio16_board_t *)dev->board_ptr)
127
128/* function prototypes */
129static int atmio16d_attach(struct comedi_device *dev,
130			   struct comedi_devconfig *it);
131static int atmio16d_detach(struct comedi_device *dev);
132static irqreturn_t atmio16d_interrupt(int irq, void *d);
133static int atmio16d_ai_cmdtest(struct comedi_device *dev,
134			       struct comedi_subdevice *s,
135			       struct comedi_cmd *cmd);
136static int atmio16d_ai_cmd(struct comedi_device *dev,
137			   struct comedi_subdevice *s);
138static int atmio16d_ai_cancel(struct comedi_device *dev,
139			      struct comedi_subdevice *s);
140static void reset_counters(struct comedi_device *dev);
141static void reset_atmio16d(struct comedi_device *dev);
142
143/* main driver struct */
144static struct comedi_driver driver_atmio16d = {
145	.driver_name = "atmio16",
146	.module = THIS_MODULE,
147	.attach = atmio16d_attach,
148	.detach = atmio16d_detach,
149	.board_name = &atmio16_boards[0].name,
150	.num_names = n_atmio16_boards,
151	.offset = sizeof(struct atmio16_board_t),
152};
153
154COMEDI_INITCLEANUP(driver_atmio16d);
155
156/* range structs */
157static const struct comedi_lrange range_atmio16d_ai_10_bipolar = { 4, {
158								       BIP_RANGE
159								       (10),
160								       BIP_RANGE
161								       (1),
162								       BIP_RANGE
163								       (0.1),
164								       BIP_RANGE
165								       (0.02)
166								       }
167};
168
169static const struct comedi_lrange range_atmio16d_ai_5_bipolar = { 4, {
170								      BIP_RANGE
171								      (5),
172								      BIP_RANGE
173								      (0.5),
174								      BIP_RANGE
175								      (0.05),
176								      BIP_RANGE
177								      (0.01)
178								      }
179};
180
181static const struct comedi_lrange range_atmio16d_ai_unipolar = { 4, {
182								     UNI_RANGE
183								     (10),
184								     UNI_RANGE
185								     (1),
186								     UNI_RANGE
187								     (0.1),
188								     UNI_RANGE
189								     (0.02)
190								     }
191};
192
193/* private data struct */
194struct atmio16d_private {
195	enum { adc_diff, adc_singleended } adc_mux;
196	enum { adc_bipolar10, adc_bipolar5, adc_unipolar10 } adc_range;
197	enum { adc_2comp, adc_straight } adc_coding;
198	enum { dac_bipolar, dac_unipolar } dac0_range, dac1_range;
199	enum { dac_internal, dac_external } dac0_reference, dac1_reference;
200	enum { dac_2comp, dac_straight } dac0_coding, dac1_coding;
201	const struct comedi_lrange *ao_range_type_list[2];
202	unsigned int ao_readback[2];
203	unsigned int com_reg_1_state; /* current state of command register 1 */
204	unsigned int com_reg_2_state; /* current state of command register 2 */
205};
206
207static void reset_counters(struct comedi_device *dev)
208{
209	/* Counter 2 */
210	outw(0xFFC2, dev->iobase + AM9513A_COM_REG);
211	outw(0xFF02, dev->iobase + AM9513A_COM_REG);
212	outw(0x4, dev->iobase + AM9513A_DATA_REG);
213	outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
214	outw(0x3, dev->iobase + AM9513A_DATA_REG);
215	outw(0xFF42, dev->iobase + AM9513A_COM_REG);
216	outw(0xFF42, dev->iobase + AM9513A_COM_REG);
217	/* Counter 3 */
218	outw(0xFFC4, dev->iobase + AM9513A_COM_REG);
219	outw(0xFF03, dev->iobase + AM9513A_COM_REG);
220	outw(0x4, dev->iobase + AM9513A_DATA_REG);
221	outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
222	outw(0x3, dev->iobase + AM9513A_DATA_REG);
223	outw(0xFF44, dev->iobase + AM9513A_COM_REG);
224	outw(0xFF44, dev->iobase + AM9513A_COM_REG);
225	/* Counter 4 */
226	outw(0xFFC8, dev->iobase + AM9513A_COM_REG);
227	outw(0xFF04, dev->iobase + AM9513A_COM_REG);
228	outw(0x4, dev->iobase + AM9513A_DATA_REG);
229	outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
230	outw(0x3, dev->iobase + AM9513A_DATA_REG);
231	outw(0xFF48, dev->iobase + AM9513A_COM_REG);
232	outw(0xFF48, dev->iobase + AM9513A_COM_REG);
233	/* Counter 5 */
234	outw(0xFFD0, dev->iobase + AM9513A_COM_REG);
235	outw(0xFF05, dev->iobase + AM9513A_COM_REG);
236	outw(0x4, dev->iobase + AM9513A_DATA_REG);
237	outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
238	outw(0x3, dev->iobase + AM9513A_DATA_REG);
239	outw(0xFF50, dev->iobase + AM9513A_COM_REG);
240	outw(0xFF50, dev->iobase + AM9513A_COM_REG);
241
242	outw(0, dev->iobase + AD_CLEAR_REG);
243}
244
245static void reset_atmio16d(struct comedi_device *dev)
246{
247	int i;
248
249	/* now we need to initialize the board */
250	outw(0, dev->iobase + COM_REG_1);
251	outw(0, dev->iobase + COM_REG_2);
252	outw(0, dev->iobase + MUX_GAIN_REG);
253	/* init AM9513A timer */
254	outw(0xFFFF, dev->iobase + AM9513A_COM_REG);
255	outw(0xFFEF, dev->iobase + AM9513A_COM_REG);
256	outw(0xFF17, dev->iobase + AM9513A_COM_REG);
257	outw(0xF000, dev->iobase + AM9513A_DATA_REG);
258	for (i = 1; i <= 5; ++i) {
259		outw(0xFF00 + i, dev->iobase + AM9513A_COM_REG);
260		outw(0x0004, dev->iobase + AM9513A_DATA_REG);
261		outw(0xFF08 + i, dev->iobase + AM9513A_COM_REG);
262		outw(0x3, dev->iobase + AM9513A_DATA_REG);
263	}
264	outw(0xFF5F, dev->iobase + AM9513A_COM_REG);
265	/* timer init done */
266	outw(0, dev->iobase + AD_CLEAR_REG);
267	outw(0, dev->iobase + INT2CLR_REG);
268	/* select straight binary mode for Analog Input */
269	devpriv->com_reg_1_state |= 1;
270	outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
271	devpriv->adc_coding = adc_straight;
272	/* zero the analog outputs */
273	outw(2048, dev->iobase + DAC0_REG);
274	outw(2048, dev->iobase + DAC1_REG);
275}
276
277static irqreturn_t atmio16d_interrupt(int irq, void *d)
278{
279	struct comedi_device *dev = d;
280	struct comedi_subdevice *s = dev->subdevices + 0;
281
282/* printk("atmio16d_interrupt!\n"); */
283
284	comedi_buf_put(s->async, inw(dev->iobase + AD_FIFO_REG));
285
286	comedi_event(dev, s);
287	return IRQ_HANDLED;
288}
289
290static int atmio16d_ai_cmdtest(struct comedi_device *dev,
291			       struct comedi_subdevice *s,
292			       struct comedi_cmd *cmd)
293{
294	int err = 0, tmp;
295#ifdef DEBUG1
296	printk("atmio16d_ai_cmdtest\n");
297#endif
298	/* make sure triggers are valid */
299	tmp = cmd->start_src;
300	cmd->start_src &= TRIG_NOW;
301	if (!cmd->start_src || tmp != cmd->start_src)
302		err++;
303
304	tmp = cmd->scan_begin_src;
305	cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER;
306	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
307		err++;
308
309	tmp = cmd->convert_src;
310	cmd->convert_src &= TRIG_TIMER;
311	if (!cmd->convert_src || tmp != cmd->convert_src)
312		err++;
313
314	tmp = cmd->scan_end_src;
315	cmd->scan_end_src &= TRIG_COUNT;
316	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
317		err++;
318
319	tmp = cmd->stop_src;
320	cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
321	if (!cmd->stop_src || tmp != cmd->stop_src)
322		err++;
323
324	if (err)
325		return 1;
326
327	/* step 2: make sure trigger sources are unique & mutually compatible */
328	/* note that mutual compatiblity is not an issue here */
329	if (cmd->scan_begin_src != TRIG_FOLLOW &&
330	    cmd->scan_begin_src != TRIG_EXT &&
331	    cmd->scan_begin_src != TRIG_TIMER)
332		err++;
333	if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
334		err++;
335
336	if (err)
337		return 2;
338
339	/* step 3: make sure arguments are trivially compatible */
340
341	if (cmd->start_arg != 0) {
342		cmd->start_arg = 0;
343		err++;
344	}
345	if (cmd->scan_begin_src == TRIG_FOLLOW) {
346		/* internal trigger */
347		if (cmd->scan_begin_arg != 0) {
348			cmd->scan_begin_arg = 0;
349			err++;
350		}
351	} else {
352#if 0
353		/* external trigger */
354		/* should be level/edge, hi/lo specification here */
355		if (cmd->scan_begin_arg != 0) {
356			cmd->scan_begin_arg = 0;
357			err++;
358		}
359#endif
360	}
361
362	if (cmd->convert_arg < 10000) {
363		cmd->convert_arg = 10000;
364		err++;
365	}
366#if 0
367	if (cmd->convert_arg > SLOWEST_TIMER) {
368		cmd->convert_arg = SLOWEST_TIMER;
369		err++;
370	}
371#endif
372	if (cmd->scan_end_arg != cmd->chanlist_len) {
373		cmd->scan_end_arg = cmd->chanlist_len;
374		err++;
375	}
376	if (cmd->stop_src == TRIG_COUNT) {
377		/* any count is allowed */
378	} else {
379		/* TRIG_NONE */
380		if (cmd->stop_arg != 0) {
381			cmd->stop_arg = 0;
382			err++;
383		}
384	}
385
386	if (err)
387		return 3;
388
389	return 0;
390}
391
392static int atmio16d_ai_cmd(struct comedi_device *dev,
393			   struct comedi_subdevice *s)
394{
395	struct comedi_cmd *cmd = &s->async->cmd;
396	unsigned int timer, base_clock;
397	unsigned int sample_count, tmp, chan, gain;
398	int i;
399#ifdef DEBUG1
400	printk("atmio16d_ai_cmd\n");
401#endif
402	/* This is slowly becoming a working command interface. *
403	 * It is still uber-experimental */
404
405	reset_counters(dev);
406	s->async->cur_chan = 0;
407
408	/* check if scanning multiple channels */
409	if (cmd->chanlist_len < 2) {
410		devpriv->com_reg_1_state &= ~COMREG1_SCANEN;
411		outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
412	} else {
413		devpriv->com_reg_1_state |= COMREG1_SCANEN;
414		devpriv->com_reg_2_state |= COMREG2_SCN2;
415		outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
416		outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
417	}
418
419	/* Setup the Mux-Gain Counter */
420	for (i = 0; i < cmd->chanlist_len; ++i) {
421		chan = CR_CHAN(cmd->chanlist[i]);
422		gain = CR_RANGE(cmd->chanlist[i]);
423		outw(i, dev->iobase + MUX_CNTR_REG);
424		tmp = chan | (gain << 6);
425		if (i == cmd->scan_end_arg - 1)
426			tmp |= 0x0010;	/* set LASTONE bit */
427		outw(tmp, dev->iobase + MUX_GAIN_REG);
428	}
429
430	/* Now program the sample interval timer */
431	/* Figure out which clock to use then get an
432	 * appropriate timer value */
433	if (cmd->convert_arg < 65536000) {
434		base_clock = CLOCK_1_MHZ;
435		timer = cmd->convert_arg / 1000;
436	} else if (cmd->convert_arg < 655360000) {
437		base_clock = CLOCK_100_KHZ;
438		timer = cmd->convert_arg / 10000;
439	} else if (cmd->convert_arg <= 0xffffffff /* 6553600000 */) {
440		base_clock = CLOCK_10_KHZ;
441		timer = cmd->convert_arg / 100000;
442	} else if (cmd->convert_arg <= 0xffffffff /* 65536000000 */) {
443		base_clock = CLOCK_1_KHZ;
444		timer = cmd->convert_arg / 1000000;
445	}
446	outw(0xFF03, dev->iobase + AM9513A_COM_REG);
447	outw(base_clock, dev->iobase + AM9513A_DATA_REG);
448	outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
449	outw(0x2, dev->iobase + AM9513A_DATA_REG);
450	outw(0xFF44, dev->iobase + AM9513A_COM_REG);
451	outw(0xFFF3, dev->iobase + AM9513A_COM_REG);
452	outw(timer, dev->iobase + AM9513A_DATA_REG);
453	outw(0xFF24, dev->iobase + AM9513A_COM_REG);
454
455	/* Now figure out how many samples to get */
456	/* and program the sample counter */
457	sample_count = cmd->stop_arg * cmd->scan_end_arg;
458	outw(0xFF04, dev->iobase + AM9513A_COM_REG);
459	outw(0x1025, dev->iobase + AM9513A_DATA_REG);
460	outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
461	if (sample_count < 65536) {
462		/* use only Counter 4 */
463		outw(sample_count, dev->iobase + AM9513A_DATA_REG);
464		outw(0xFF48, dev->iobase + AM9513A_COM_REG);
465		outw(0xFFF4, dev->iobase + AM9513A_COM_REG);
466		outw(0xFF28, dev->iobase + AM9513A_COM_REG);
467		devpriv->com_reg_1_state &= ~COMREG1_1632CNT;
468		outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
469	} else {
470		/* Counter 4 and 5 are needed */
471
472		tmp = sample_count & 0xFFFF;
473		if (tmp)
474			outw(tmp - 1, dev->iobase + AM9513A_DATA_REG);
475		else
476			outw(0xFFFF, dev->iobase + AM9513A_DATA_REG);
477
478		outw(0xFF48, dev->iobase + AM9513A_COM_REG);
479		outw(0, dev->iobase + AM9513A_DATA_REG);
480		outw(0xFF28, dev->iobase + AM9513A_COM_REG);
481		outw(0xFF05, dev->iobase + AM9513A_COM_REG);
482		outw(0x25, dev->iobase + AM9513A_DATA_REG);
483		outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
484		tmp = sample_count & 0xFFFF;
485		if ((tmp == 0) || (tmp == 1)) {
486			outw((sample_count >> 16) & 0xFFFF,
487			     dev->iobase + AM9513A_DATA_REG);
488		} else {
489			outw(((sample_count >> 16) & 0xFFFF) + 1,
490			     dev->iobase + AM9513A_DATA_REG);
491		}
492		outw(0xFF70, dev->iobase + AM9513A_COM_REG);
493		devpriv->com_reg_1_state |= COMREG1_1632CNT;
494		outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
495	}
496
497	/* Program the scan interval timer ONLY IF SCANNING IS ENABLED */
498	/* Figure out which clock to use then get an
499	 * appropriate timer value */
500	if (cmd->chanlist_len > 1) {
501		if (cmd->scan_begin_arg < 65536000) {
502			base_clock = CLOCK_1_MHZ;
503			timer = cmd->scan_begin_arg / 1000;
504		} else if (cmd->scan_begin_arg < 655360000) {
505			base_clock = CLOCK_100_KHZ;
506			timer = cmd->scan_begin_arg / 10000;
507		} else if (cmd->scan_begin_arg < 0xffffffff /* 6553600000 */) {
508			base_clock = CLOCK_10_KHZ;
509			timer = cmd->scan_begin_arg / 100000;
510		} else if (cmd->scan_begin_arg < 0xffffffff /* 65536000000 */) {
511			base_clock = CLOCK_1_KHZ;
512			timer = cmd->scan_begin_arg / 1000000;
513		}
514		outw(0xFF02, dev->iobase + AM9513A_COM_REG);
515		outw(base_clock, dev->iobase + AM9513A_DATA_REG);
516		outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
517		outw(0x2, dev->iobase + AM9513A_DATA_REG);
518		outw(0xFF42, dev->iobase + AM9513A_COM_REG);
519		outw(0xFFF2, dev->iobase + AM9513A_COM_REG);
520		outw(timer, dev->iobase + AM9513A_DATA_REG);
521		outw(0xFF22, dev->iobase + AM9513A_COM_REG);
522	}
523
524	/* Clear the A/D FIFO and reset the MUX counter */
525	outw(0, dev->iobase + AD_CLEAR_REG);
526	outw(0, dev->iobase + MUX_CNTR_REG);
527	outw(0, dev->iobase + INT2CLR_REG);
528	/* enable this acquisition operation */
529	devpriv->com_reg_1_state |= COMREG1_DAQEN;
530	outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
531	/* enable interrupts for conversion completion */
532	devpriv->com_reg_1_state |= COMREG1_CONVINTEN;
533	devpriv->com_reg_2_state |= COMREG2_INTEN;
534	outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
535	outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
536	/* apply a trigger. this starts the counters! */
537	outw(0, dev->iobase + START_DAQ_REG);
538
539	return 0;
540}
541
542/* This will cancel a running acquisition operation */
543static int atmio16d_ai_cancel(struct comedi_device *dev,
544			      struct comedi_subdevice *s)
545{
546	reset_atmio16d(dev);
547
548	return 0;
549}
550
551/* Mode 0 is used to get a single conversion on demand */
552static int atmio16d_ai_insn_read(struct comedi_device *dev,
553				 struct comedi_subdevice *s,
554				 struct comedi_insn *insn, unsigned int *data)
555{
556	int i, t;
557	int chan;
558	int gain;
559	int status;
560
561#ifdef DEBUG1
562	printk("atmio16d_ai_insn_read\n");
563#endif
564	chan = CR_CHAN(insn->chanspec);
565	gain = CR_RANGE(insn->chanspec);
566
567	/* reset the Analog input circuitry */
568	/* outw( 0, dev->iobase+AD_CLEAR_REG ); */
569	/* reset the Analog Input MUX Counter to 0 */
570	/* outw( 0, dev->iobase+MUX_CNTR_REG ); */
571
572	/* set the Input MUX gain */
573	outw(chan | (gain << 6), dev->iobase + MUX_GAIN_REG);
574
575	for (i = 0; i < insn->n; i++) {
576		/* start the conversion */
577		outw(0, dev->iobase + START_CONVERT_REG);
578		/* wait for it to finish */
579		for (t = 0; t < ATMIO16D_TIMEOUT; t++) {
580			/* check conversion status */
581			status = inw(dev->iobase + STAT_REG);
582#ifdef DEBUG1
583			printk("status=%x\n", status);
584#endif
585			if (status & STAT_AD_CONVAVAIL) {
586				/* read the data now */
587				data[i] = inw(dev->iobase + AD_FIFO_REG);
588				/* change to two's complement if need be */
589				if (devpriv->adc_coding == adc_2comp) {
590					data[i] ^= 0x800;
591				}
592				break;
593			}
594			if (status & STAT_AD_OVERFLOW) {
595				printk("atmio16d: a/d FIFO overflow\n");
596				outw(0, dev->iobase + AD_CLEAR_REG);
597
598				return -ETIME;
599			}
600		}
601		/* end waiting, now check if it timed out */
602		if (t == ATMIO16D_TIMEOUT) {
603			printk("atmio16d: timeout\n");
604
605			return -ETIME;
606		}
607	}
608
609	return i;
610}
611
612static int atmio16d_ao_insn_read(struct comedi_device *dev,
613				 struct comedi_subdevice *s,
614				 struct comedi_insn *insn, unsigned int *data)
615{
616	int i;
617#ifdef DEBUG1
618	printk("atmio16d_ao_insn_read\n");
619#endif
620
621	for (i = 0; i < insn->n; i++) {
622		data[i] = devpriv->ao_readback[CR_CHAN(insn->chanspec)];
623	}
624
625	return i;
626}
627
628static int atmio16d_ao_insn_write(struct comedi_device *dev,
629				  struct comedi_subdevice *s,
630				  struct comedi_insn *insn, unsigned int *data)
631{
632	int i;
633	int chan;
634	int d;
635#ifdef DEBUG1
636	printk("atmio16d_ao_insn_write\n");
637#endif
638
639	chan = CR_CHAN(insn->chanspec);
640
641	for (i = 0; i < insn->n; i++) {
642		d = data[i];
643		switch (chan) {
644		case 0:
645			if (devpriv->dac0_coding == dac_2comp) {
646				d ^= 0x800;
647			}
648			outw(d, dev->iobase + DAC0_REG);
649			break;
650		case 1:
651			if (devpriv->dac1_coding == dac_2comp) {
652				d ^= 0x800;
653			}
654			outw(d, dev->iobase + DAC1_REG);
655			break;
656		default:
657			return -EINVAL;
658		}
659		devpriv->ao_readback[chan] = data[i];
660	}
661	return i;
662}
663
664static int atmio16d_dio_insn_bits(struct comedi_device *dev,
665				  struct comedi_subdevice *s,
666				  struct comedi_insn *insn, unsigned int *data)
667{
668	if (insn->n != 2)
669		return -EINVAL;
670
671	if (data[0]) {
672		s->state &= ~data[0];
673		s->state |= (data[0] | data[1]);
674		outw(s->state, dev->iobase + MIO_16_DIG_OUT_REG);
675	}
676	data[1] = inw(dev->iobase + MIO_16_DIG_IN_REG);
677
678	return 2;
679}
680
681static int atmio16d_dio_insn_config(struct comedi_device *dev,
682				    struct comedi_subdevice *s,
683				    struct comedi_insn *insn,
684				    unsigned int *data)
685{
686	int i;
687	int mask;
688
689	for (i = 0; i < insn->n; i++) {
690		mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
691		s->io_bits &= ~mask;
692		if (data[i])
693			s->io_bits |= mask;
694	}
695	devpriv->com_reg_2_state &= ~(COMREG2_DOUTEN0 | COMREG2_DOUTEN1);
696	if (s->io_bits & 0x0f)
697		devpriv->com_reg_2_state |= COMREG2_DOUTEN0;
698	if (s->io_bits & 0xf0)
699		devpriv->com_reg_2_state |= COMREG2_DOUTEN1;
700	outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
701
702	return i;
703}
704
705/*
706   options[0] - I/O port
707   options[1] - MIO irq
708                0 == no irq
709                N == irq N {3,4,5,6,7,9,10,11,12,14,15}
710   options[2] - DIO irq
711                0 == no irq
712                N == irq N {3,4,5,6,7,9}
713   options[3] - DMA1 channel
714                0 == no DMA
715                N == DMA N {5,6,7}
716   options[4] - DMA2 channel
717                0 == no DMA
718                N == DMA N {5,6,7}
719
720   options[5] - a/d mux
721   	0=differential, 1=single
722   options[6] - a/d range
723   	0=bipolar10, 1=bipolar5, 2=unipolar10
724
725   options[7] - dac0 range
726   	0=bipolar, 1=unipolar
727   options[8] - dac0 reference
728    0=internal, 1=external
729   options[9] - dac0 coding
730   	0=2's comp, 1=straight binary
731
732   options[10] - dac1 range
733   options[11] - dac1 reference
734   options[12] - dac1 coding
735 */
736
737static int atmio16d_attach(struct comedi_device *dev,
738			   struct comedi_devconfig *it)
739{
740	unsigned int irq;
741	unsigned long iobase;
742	int ret;
743
744	struct comedi_subdevice *s;
745
746	/* make sure the address range is free and allocate it */
747	iobase = it->options[0];
748	printk("comedi%d: atmio16d: 0x%04lx ", dev->minor, iobase);
749	if (!request_region(iobase, ATMIO16D_SIZE, "ni_atmio16d")) {
750		printk("I/O port conflict\n");
751		return -EIO;
752	}
753	dev->iobase = iobase;
754
755	/* board name */
756	dev->board_name = boardtype->name;
757
758	ret = alloc_subdevices(dev, 4);
759	if (ret < 0)
760		return ret;
761
762	ret = alloc_private(dev, sizeof(struct atmio16d_private));
763	if (ret < 0)
764		return ret;
765
766	/* reset the atmio16d hardware */
767	reset_atmio16d(dev);
768
769	/* check if our interrupt is available and get it */
770	irq = it->options[1];
771	if (irq) {
772
773		ret = request_irq(irq, atmio16d_interrupt, 0, "atmio16d", dev);
774		if (ret < 0) {
775			printk("failed to allocate irq %u\n", irq);
776			return ret;
777		}
778		dev->irq = irq;
779		printk("( irq = %u )\n", irq);
780	} else {
781		printk("( no irq )");
782	}
783
784	/* set device options */
785	devpriv->adc_mux = it->options[5];
786	devpriv->adc_range = it->options[6];
787
788	devpriv->dac0_range = it->options[7];
789	devpriv->dac0_reference = it->options[8];
790	devpriv->dac0_coding = it->options[9];
791	devpriv->dac1_range = it->options[10];
792	devpriv->dac1_reference = it->options[11];
793	devpriv->dac1_coding = it->options[12];
794
795	/* setup sub-devices */
796	s = dev->subdevices + 0;
797	dev->read_subdev = s;
798	/* ai subdevice */
799	s->type = COMEDI_SUBD_AI;
800	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
801	s->n_chan = (devpriv->adc_mux ? 16 : 8);
802	s->len_chanlist = 16;
803	s->insn_read = atmio16d_ai_insn_read;
804	s->do_cmdtest = atmio16d_ai_cmdtest;
805	s->do_cmd = atmio16d_ai_cmd;
806	s->cancel = atmio16d_ai_cancel;
807	s->maxdata = 0xfff;	/* 4095 decimal */
808	switch (devpriv->adc_range) {
809	case adc_bipolar10:
810		s->range_table = &range_atmio16d_ai_10_bipolar;
811		break;
812	case adc_bipolar5:
813		s->range_table = &range_atmio16d_ai_5_bipolar;
814		break;
815	case adc_unipolar10:
816		s->range_table = &range_atmio16d_ai_unipolar;
817		break;
818	}
819
820	/* ao subdevice */
821	s++;
822	s->type = COMEDI_SUBD_AO;
823	s->subdev_flags = SDF_WRITABLE;
824	s->n_chan = 2;
825	s->insn_read = atmio16d_ao_insn_read;
826	s->insn_write = atmio16d_ao_insn_write;
827	s->maxdata = 0xfff;	/* 4095 decimal */
828	s->range_table_list = devpriv->ao_range_type_list;
829	switch (devpriv->dac0_range) {
830	case dac_bipolar:
831		devpriv->ao_range_type_list[0] = &range_bipolar10;
832		break;
833	case dac_unipolar:
834		devpriv->ao_range_type_list[0] = &range_unipolar10;
835		break;
836	}
837	switch (devpriv->dac1_range) {
838	case dac_bipolar:
839		devpriv->ao_range_type_list[1] = &range_bipolar10;
840		break;
841	case dac_unipolar:
842		devpriv->ao_range_type_list[1] = &range_unipolar10;
843		break;
844	}
845
846	/* Digital I/O */
847	s++;
848	s->type = COMEDI_SUBD_DIO;
849	s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
850	s->n_chan = 8;
851	s->insn_bits = atmio16d_dio_insn_bits;
852	s->insn_config = atmio16d_dio_insn_config;
853	s->maxdata = 1;
854	s->range_table = &range_digital;
855
856	/* 8255 subdevice */
857	s++;
858	if (boardtype->has_8255) {
859		subdev_8255_init(dev, s, NULL, dev->iobase);
860	} else {
861		s->type = COMEDI_SUBD_UNUSED;
862	}
863
864/* don't yet know how to deal with counter/timers */
865#if 0
866	s++;
867	/* do */
868	s->type = COMEDI_SUBD_TIMER;
869	s->n_chan = 0;
870	s->maxdata = 0
871#endif
872	    printk("\n");
873
874	return 0;
875}
876
877static int atmio16d_detach(struct comedi_device *dev)
878{
879	printk("comedi%d: atmio16d: remove\n", dev->minor);
880
881	if (dev->subdevices && boardtype->has_8255)
882		subdev_8255_cleanup(dev, dev->subdevices + 3);
883
884	if (dev->irq)
885		free_irq(dev->irq, dev);
886
887	reset_atmio16d(dev);
888
889	if (dev->iobase)
890		release_region(dev->iobase, ATMIO16D_SIZE);
891
892	return 0;
893}
894