ni_atmio16d.c revision ce5ade4f74139aad964ee4c15017a5725e3d3511
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				break;
592			}
593			if (status & STAT_AD_OVERFLOW) {
594				printk("atmio16d: a/d FIFO overflow\n");
595				outw(0, dev->iobase + AD_CLEAR_REG);
596
597				return -ETIME;
598			}
599		}
600		/* end waiting, now check if it timed out */
601		if (t == ATMIO16D_TIMEOUT) {
602			printk("atmio16d: timeout\n");
603
604			return -ETIME;
605		}
606	}
607
608	return i;
609}
610
611static int atmio16d_ao_insn_read(struct comedi_device *dev,
612				 struct comedi_subdevice *s,
613				 struct comedi_insn *insn, unsigned int *data)
614{
615	int i;
616#ifdef DEBUG1
617	printk("atmio16d_ao_insn_read\n");
618#endif
619
620	for (i = 0; i < insn->n; i++)
621		data[i] = devpriv->ao_readback[CR_CHAN(insn->chanspec)];
622	return i;
623}
624
625static int atmio16d_ao_insn_write(struct comedi_device *dev,
626				  struct comedi_subdevice *s,
627				  struct comedi_insn *insn, unsigned int *data)
628{
629	int i;
630	int chan;
631	int d;
632#ifdef DEBUG1
633	printk("atmio16d_ao_insn_write\n");
634#endif
635
636	chan = CR_CHAN(insn->chanspec);
637
638	for (i = 0; i < insn->n; i++) {
639		d = data[i];
640		switch (chan) {
641		case 0:
642			if (devpriv->dac0_coding == dac_2comp)
643				d ^= 0x800;
644			outw(d, dev->iobase + DAC0_REG);
645			break;
646		case 1:
647			if (devpriv->dac1_coding == dac_2comp)
648				d ^= 0x800;
649			outw(d, dev->iobase + DAC1_REG);
650			break;
651		default:
652			return -EINVAL;
653		}
654		devpriv->ao_readback[chan] = data[i];
655	}
656	return i;
657}
658
659static int atmio16d_dio_insn_bits(struct comedi_device *dev,
660				  struct comedi_subdevice *s,
661				  struct comedi_insn *insn, unsigned int *data)
662{
663	if (insn->n != 2)
664		return -EINVAL;
665
666	if (data[0]) {
667		s->state &= ~data[0];
668		s->state |= (data[0] | data[1]);
669		outw(s->state, dev->iobase + MIO_16_DIG_OUT_REG);
670	}
671	data[1] = inw(dev->iobase + MIO_16_DIG_IN_REG);
672
673	return 2;
674}
675
676static int atmio16d_dio_insn_config(struct comedi_device *dev,
677				    struct comedi_subdevice *s,
678				    struct comedi_insn *insn,
679				    unsigned int *data)
680{
681	int i;
682	int mask;
683
684	for (i = 0; i < insn->n; i++) {
685		mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
686		s->io_bits &= ~mask;
687		if (data[i])
688			s->io_bits |= mask;
689	}
690	devpriv->com_reg_2_state &= ~(COMREG2_DOUTEN0 | COMREG2_DOUTEN1);
691	if (s->io_bits & 0x0f)
692		devpriv->com_reg_2_state |= COMREG2_DOUTEN0;
693	if (s->io_bits & 0xf0)
694		devpriv->com_reg_2_state |= COMREG2_DOUTEN1;
695	outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
696
697	return i;
698}
699
700/*
701   options[0] - I/O port
702   options[1] - MIO irq
703		0 == no irq
704		N == irq N {3,4,5,6,7,9,10,11,12,14,15}
705   options[2] - DIO irq
706		0 == no irq
707		N == irq N {3,4,5,6,7,9}
708   options[3] - DMA1 channel
709		0 == no DMA
710		N == DMA N {5,6,7}
711   options[4] - DMA2 channel
712		0 == no DMA
713		N == DMA N {5,6,7}
714
715   options[5] - a/d mux
716	0=differential, 1=single
717   options[6] - a/d range
718	0=bipolar10, 1=bipolar5, 2=unipolar10
719
720   options[7] - dac0 range
721	0=bipolar, 1=unipolar
722   options[8] - dac0 reference
723	0=internal, 1=external
724   options[9] - dac0 coding
725	0=2's comp, 1=straight binary
726
727   options[10] - dac1 range
728   options[11] - dac1 reference
729   options[12] - dac1 coding
730 */
731
732static int atmio16d_attach(struct comedi_device *dev,
733			   struct comedi_devconfig *it)
734{
735	unsigned int irq;
736	unsigned long iobase;
737	int ret;
738
739	struct comedi_subdevice *s;
740
741	/* make sure the address range is free and allocate it */
742	iobase = it->options[0];
743	printk("comedi%d: atmio16d: 0x%04lx ", dev->minor, iobase);
744	if (!request_region(iobase, ATMIO16D_SIZE, "ni_atmio16d")) {
745		printk("I/O port conflict\n");
746		return -EIO;
747	}
748	dev->iobase = iobase;
749
750	/* board name */
751	dev->board_name = boardtype->name;
752
753	ret = alloc_subdevices(dev, 4);
754	if (ret < 0)
755		return ret;
756
757	ret = alloc_private(dev, sizeof(struct atmio16d_private));
758	if (ret < 0)
759		return ret;
760
761	/* reset the atmio16d hardware */
762	reset_atmio16d(dev);
763
764	/* check if our interrupt is available and get it */
765	irq = it->options[1];
766	if (irq) {
767
768		ret = request_irq(irq, atmio16d_interrupt, 0, "atmio16d", dev);
769		if (ret < 0) {
770			printk("failed to allocate irq %u\n", irq);
771			return ret;
772		}
773		dev->irq = irq;
774		printk("( irq = %u )\n", irq);
775	} else {
776		printk("( no irq )");
777	}
778
779	/* set device options */
780	devpriv->adc_mux = it->options[5];
781	devpriv->adc_range = it->options[6];
782
783	devpriv->dac0_range = it->options[7];
784	devpriv->dac0_reference = it->options[8];
785	devpriv->dac0_coding = it->options[9];
786	devpriv->dac1_range = it->options[10];
787	devpriv->dac1_reference = it->options[11];
788	devpriv->dac1_coding = it->options[12];
789
790	/* setup sub-devices */
791	s = dev->subdevices + 0;
792	dev->read_subdev = s;
793	/* ai subdevice */
794	s->type = COMEDI_SUBD_AI;
795	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
796	s->n_chan = (devpriv->adc_mux ? 16 : 8);
797	s->len_chanlist = 16;
798	s->insn_read = atmio16d_ai_insn_read;
799	s->do_cmdtest = atmio16d_ai_cmdtest;
800	s->do_cmd = atmio16d_ai_cmd;
801	s->cancel = atmio16d_ai_cancel;
802	s->maxdata = 0xfff;	/* 4095 decimal */
803	switch (devpriv->adc_range) {
804	case adc_bipolar10:
805		s->range_table = &range_atmio16d_ai_10_bipolar;
806		break;
807	case adc_bipolar5:
808		s->range_table = &range_atmio16d_ai_5_bipolar;
809		break;
810	case adc_unipolar10:
811		s->range_table = &range_atmio16d_ai_unipolar;
812		break;
813	}
814
815	/* ao subdevice */
816	s++;
817	s->type = COMEDI_SUBD_AO;
818	s->subdev_flags = SDF_WRITABLE;
819	s->n_chan = 2;
820	s->insn_read = atmio16d_ao_insn_read;
821	s->insn_write = atmio16d_ao_insn_write;
822	s->maxdata = 0xfff;	/* 4095 decimal */
823	s->range_table_list = devpriv->ao_range_type_list;
824	switch (devpriv->dac0_range) {
825	case dac_bipolar:
826		devpriv->ao_range_type_list[0] = &range_bipolar10;
827		break;
828	case dac_unipolar:
829		devpriv->ao_range_type_list[0] = &range_unipolar10;
830		break;
831	}
832	switch (devpriv->dac1_range) {
833	case dac_bipolar:
834		devpriv->ao_range_type_list[1] = &range_bipolar10;
835		break;
836	case dac_unipolar:
837		devpriv->ao_range_type_list[1] = &range_unipolar10;
838		break;
839	}
840
841	/* Digital I/O */
842	s++;
843	s->type = COMEDI_SUBD_DIO;
844	s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
845	s->n_chan = 8;
846	s->insn_bits = atmio16d_dio_insn_bits;
847	s->insn_config = atmio16d_dio_insn_config;
848	s->maxdata = 1;
849	s->range_table = &range_digital;
850
851	/* 8255 subdevice */
852	s++;
853	if (boardtype->has_8255)
854		subdev_8255_init(dev, s, NULL, dev->iobase);
855	else
856		s->type = COMEDI_SUBD_UNUSED;
857
858/* don't yet know how to deal with counter/timers */
859#if 0
860	s++;
861	/* do */
862	s->type = COMEDI_SUBD_TIMER;
863	s->n_chan = 0;
864	s->maxdata = 0
865#endif
866	    printk("\n");
867
868	return 0;
869}
870
871static int atmio16d_detach(struct comedi_device *dev)
872{
873	printk("comedi%d: atmio16d: remove\n", dev->minor);
874
875	if (dev->subdevices && boardtype->has_8255)
876		subdev_8255_cleanup(dev, dev->subdevices + 3);
877
878	if (dev->irq)
879		free_irq(dev->irq, dev);
880
881	reset_atmio16d(dev);
882
883	if (dev->iobase)
884		release_region(dev->iobase, ATMIO16D_SIZE);
885
886	return 0;
887}
888