ni_atmio16d.c revision 828684f9a6e096f9150bad523c43b75d74b9badd
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#ifdef DEBUG1
283	printk(KERN_DEBUG "atmio16d_interrupt!\n");
284#endif
285
286	comedi_buf_put(s->async, inw(dev->iobase + AD_FIFO_REG));
287
288	comedi_event(dev, s);
289	return IRQ_HANDLED;
290}
291
292static int atmio16d_ai_cmdtest(struct comedi_device *dev,
293			       struct comedi_subdevice *s,
294			       struct comedi_cmd *cmd)
295{
296	int err = 0, tmp;
297#ifdef DEBUG1
298	printk(KERN_DEBUG "atmio16d_ai_cmdtest\n");
299#endif
300	/* make sure triggers are valid */
301	tmp = cmd->start_src;
302	cmd->start_src &= TRIG_NOW;
303	if (!cmd->start_src || tmp != cmd->start_src)
304		err++;
305
306	tmp = cmd->scan_begin_src;
307	cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER;
308	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
309		err++;
310
311	tmp = cmd->convert_src;
312	cmd->convert_src &= TRIG_TIMER;
313	if (!cmd->convert_src || tmp != cmd->convert_src)
314		err++;
315
316	tmp = cmd->scan_end_src;
317	cmd->scan_end_src &= TRIG_COUNT;
318	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
319		err++;
320
321	tmp = cmd->stop_src;
322	cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
323	if (!cmd->stop_src || tmp != cmd->stop_src)
324		err++;
325
326	if (err)
327		return 1;
328
329	/* step 2: make sure trigger sources are unique & mutually compatible */
330	/* note that mutual compatibility is not an issue here */
331	if (cmd->scan_begin_src != TRIG_FOLLOW &&
332	    cmd->scan_begin_src != TRIG_EXT &&
333	    cmd->scan_begin_src != TRIG_TIMER)
334		err++;
335	if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
336		err++;
337
338	if (err)
339		return 2;
340
341	/* step 3: make sure arguments are trivially compatible */
342
343	if (cmd->start_arg != 0) {
344		cmd->start_arg = 0;
345		err++;
346	}
347	if (cmd->scan_begin_src == TRIG_FOLLOW) {
348		/* internal trigger */
349		if (cmd->scan_begin_arg != 0) {
350			cmd->scan_begin_arg = 0;
351			err++;
352		}
353	} else {
354#if 0
355		/* external trigger */
356		/* should be level/edge, hi/lo specification here */
357		if (cmd->scan_begin_arg != 0) {
358			cmd->scan_begin_arg = 0;
359			err++;
360		}
361#endif
362	}
363
364	if (cmd->convert_arg < 10000) {
365		cmd->convert_arg = 10000;
366		err++;
367	}
368#if 0
369	if (cmd->convert_arg > SLOWEST_TIMER) {
370		cmd->convert_arg = SLOWEST_TIMER;
371		err++;
372	}
373#endif
374	if (cmd->scan_end_arg != cmd->chanlist_len) {
375		cmd->scan_end_arg = cmd->chanlist_len;
376		err++;
377	}
378	if (cmd->stop_src == TRIG_COUNT) {
379		/* any count is allowed */
380	} else {
381		/* TRIG_NONE */
382		if (cmd->stop_arg != 0) {
383			cmd->stop_arg = 0;
384			err++;
385		}
386	}
387
388	if (err)
389		return 3;
390
391	return 0;
392}
393
394static int atmio16d_ai_cmd(struct comedi_device *dev,
395			   struct comedi_subdevice *s)
396{
397	struct comedi_cmd *cmd = &s->async->cmd;
398	unsigned int timer, base_clock;
399	unsigned int sample_count, tmp, chan, gain;
400	int i;
401#ifdef DEBUG1
402	printk(KERN_DEBUG "atmio16d_ai_cmd\n");
403#endif
404	/* This is slowly becoming a working command interface. *
405	 * It is still uber-experimental */
406
407	reset_counters(dev);
408	s->async->cur_chan = 0;
409
410	/* check if scanning multiple channels */
411	if (cmd->chanlist_len < 2) {
412		devpriv->com_reg_1_state &= ~COMREG1_SCANEN;
413		outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
414	} else {
415		devpriv->com_reg_1_state |= COMREG1_SCANEN;
416		devpriv->com_reg_2_state |= COMREG2_SCN2;
417		outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
418		outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
419	}
420
421	/* Setup the Mux-Gain Counter */
422	for (i = 0; i < cmd->chanlist_len; ++i) {
423		chan = CR_CHAN(cmd->chanlist[i]);
424		gain = CR_RANGE(cmd->chanlist[i]);
425		outw(i, dev->iobase + MUX_CNTR_REG);
426		tmp = chan | (gain << 6);
427		if (i == cmd->scan_end_arg - 1)
428			tmp |= 0x0010;	/* set LASTONE bit */
429		outw(tmp, dev->iobase + MUX_GAIN_REG);
430	}
431
432	/* Now program the sample interval timer */
433	/* Figure out which clock to use then get an
434	 * appropriate timer value */
435	if (cmd->convert_arg < 65536000) {
436		base_clock = CLOCK_1_MHZ;
437		timer = cmd->convert_arg / 1000;
438	} else if (cmd->convert_arg < 655360000) {
439		base_clock = CLOCK_100_KHZ;
440		timer = cmd->convert_arg / 10000;
441	} else if (cmd->convert_arg <= 0xffffffff /* 6553600000 */) {
442		base_clock = CLOCK_10_KHZ;
443		timer = cmd->convert_arg / 100000;
444	} else if (cmd->convert_arg <= 0xffffffff /* 65536000000 */) {
445		base_clock = CLOCK_1_KHZ;
446		timer = cmd->convert_arg / 1000000;
447	}
448	outw(0xFF03, dev->iobase + AM9513A_COM_REG);
449	outw(base_clock, dev->iobase + AM9513A_DATA_REG);
450	outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
451	outw(0x2, dev->iobase + AM9513A_DATA_REG);
452	outw(0xFF44, dev->iobase + AM9513A_COM_REG);
453	outw(0xFFF3, dev->iobase + AM9513A_COM_REG);
454	outw(timer, dev->iobase + AM9513A_DATA_REG);
455	outw(0xFF24, dev->iobase + AM9513A_COM_REG);
456
457	/* Now figure out how many samples to get */
458	/* and program the sample counter */
459	sample_count = cmd->stop_arg * cmd->scan_end_arg;
460	outw(0xFF04, dev->iobase + AM9513A_COM_REG);
461	outw(0x1025, dev->iobase + AM9513A_DATA_REG);
462	outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
463	if (sample_count < 65536) {
464		/* use only Counter 4 */
465		outw(sample_count, dev->iobase + AM9513A_DATA_REG);
466		outw(0xFF48, dev->iobase + AM9513A_COM_REG);
467		outw(0xFFF4, dev->iobase + AM9513A_COM_REG);
468		outw(0xFF28, dev->iobase + AM9513A_COM_REG);
469		devpriv->com_reg_1_state &= ~COMREG1_1632CNT;
470		outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
471	} else {
472		/* Counter 4 and 5 are needed */
473
474		tmp = sample_count & 0xFFFF;
475		if (tmp)
476			outw(tmp - 1, dev->iobase + AM9513A_DATA_REG);
477		else
478			outw(0xFFFF, dev->iobase + AM9513A_DATA_REG);
479
480		outw(0xFF48, dev->iobase + AM9513A_COM_REG);
481		outw(0, dev->iobase + AM9513A_DATA_REG);
482		outw(0xFF28, dev->iobase + AM9513A_COM_REG);
483		outw(0xFF05, dev->iobase + AM9513A_COM_REG);
484		outw(0x25, dev->iobase + AM9513A_DATA_REG);
485		outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
486		tmp = sample_count & 0xFFFF;
487		if ((tmp == 0) || (tmp == 1)) {
488			outw((sample_count >> 16) & 0xFFFF,
489			     dev->iobase + AM9513A_DATA_REG);
490		} else {
491			outw(((sample_count >> 16) & 0xFFFF) + 1,
492			     dev->iobase + AM9513A_DATA_REG);
493		}
494		outw(0xFF70, dev->iobase + AM9513A_COM_REG);
495		devpriv->com_reg_1_state |= COMREG1_1632CNT;
496		outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
497	}
498
499	/* Program the scan interval timer ONLY IF SCANNING IS ENABLED */
500	/* Figure out which clock to use then get an
501	 * appropriate timer value */
502	if (cmd->chanlist_len > 1) {
503		if (cmd->scan_begin_arg < 65536000) {
504			base_clock = CLOCK_1_MHZ;
505			timer = cmd->scan_begin_arg / 1000;
506		} else if (cmd->scan_begin_arg < 655360000) {
507			base_clock = CLOCK_100_KHZ;
508			timer = cmd->scan_begin_arg / 10000;
509		} else if (cmd->scan_begin_arg < 0xffffffff /* 6553600000 */) {
510			base_clock = CLOCK_10_KHZ;
511			timer = cmd->scan_begin_arg / 100000;
512		} else if (cmd->scan_begin_arg < 0xffffffff /* 65536000000 */) {
513			base_clock = CLOCK_1_KHZ;
514			timer = cmd->scan_begin_arg / 1000000;
515		}
516		outw(0xFF02, dev->iobase + AM9513A_COM_REG);
517		outw(base_clock, dev->iobase + AM9513A_DATA_REG);
518		outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
519		outw(0x2, dev->iobase + AM9513A_DATA_REG);
520		outw(0xFF42, dev->iobase + AM9513A_COM_REG);
521		outw(0xFFF2, dev->iobase + AM9513A_COM_REG);
522		outw(timer, dev->iobase + AM9513A_DATA_REG);
523		outw(0xFF22, dev->iobase + AM9513A_COM_REG);
524	}
525
526	/* Clear the A/D FIFO and reset the MUX counter */
527	outw(0, dev->iobase + AD_CLEAR_REG);
528	outw(0, dev->iobase + MUX_CNTR_REG);
529	outw(0, dev->iobase + INT2CLR_REG);
530	/* enable this acquisition operation */
531	devpriv->com_reg_1_state |= COMREG1_DAQEN;
532	outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
533	/* enable interrupts for conversion completion */
534	devpriv->com_reg_1_state |= COMREG1_CONVINTEN;
535	devpriv->com_reg_2_state |= COMREG2_INTEN;
536	outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
537	outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
538	/* apply a trigger. this starts the counters! */
539	outw(0, dev->iobase + START_DAQ_REG);
540
541	return 0;
542}
543
544/* This will cancel a running acquisition operation */
545static int atmio16d_ai_cancel(struct comedi_device *dev,
546			      struct comedi_subdevice *s)
547{
548	reset_atmio16d(dev);
549
550	return 0;
551}
552
553/* Mode 0 is used to get a single conversion on demand */
554static int atmio16d_ai_insn_read(struct comedi_device *dev,
555				 struct comedi_subdevice *s,
556				 struct comedi_insn *insn, unsigned int *data)
557{
558	int i, t;
559	int chan;
560	int gain;
561	int status;
562
563#ifdef DEBUG1
564	printk(KERN_DEBUG "atmio16d_ai_insn_read\n");
565#endif
566	chan = CR_CHAN(insn->chanspec);
567	gain = CR_RANGE(insn->chanspec);
568
569	/* reset the Analog input circuitry */
570	/* outw( 0, dev->iobase+AD_CLEAR_REG ); */
571	/* reset the Analog Input MUX Counter to 0 */
572	/* outw( 0, dev->iobase+MUX_CNTR_REG ); */
573
574	/* set the Input MUX gain */
575	outw(chan | (gain << 6), dev->iobase + MUX_GAIN_REG);
576
577	for (i = 0; i < insn->n; i++) {
578		/* start the conversion */
579		outw(0, dev->iobase + START_CONVERT_REG);
580		/* wait for it to finish */
581		for (t = 0; t < ATMIO16D_TIMEOUT; t++) {
582			/* check conversion status */
583			status = inw(dev->iobase + STAT_REG);
584#ifdef DEBUG1
585			printk(KERN_DEBUG "status=%x\n", status);
586#endif
587			if (status & STAT_AD_CONVAVAIL) {
588				/* read the data now */
589				data[i] = inw(dev->iobase + AD_FIFO_REG);
590				/* change to two's complement if need be */
591				if (devpriv->adc_coding == adc_2comp)
592					data[i] ^= 0x800;
593				break;
594			}
595			if (status & STAT_AD_OVERFLOW) {
596				printk(KERN_INFO "atmio16d: a/d FIFO overflow\n");
597				outw(0, dev->iobase + AD_CLEAR_REG);
598
599				return -ETIME;
600			}
601		}
602		/* end waiting, now check if it timed out */
603		if (t == ATMIO16D_TIMEOUT) {
604			printk(KERN_INFO "atmio16d: timeout\n");
605
606			return -ETIME;
607		}
608	}
609
610	return i;
611}
612
613static int atmio16d_ao_insn_read(struct comedi_device *dev,
614				 struct comedi_subdevice *s,
615				 struct comedi_insn *insn, unsigned int *data)
616{
617	int i;
618#ifdef DEBUG1
619	printk(KERN_DEBUG "atmio16d_ao_insn_read\n");
620#endif
621
622	for (i = 0; i < insn->n; i++)
623		data[i] = devpriv->ao_readback[CR_CHAN(insn->chanspec)];
624	return i;
625}
626
627static int atmio16d_ao_insn_write(struct comedi_device *dev,
628				  struct comedi_subdevice *s,
629				  struct comedi_insn *insn, unsigned int *data)
630{
631	int i;
632	int chan;
633	int d;
634#ifdef DEBUG1
635	printk(KERN_DEBUG "atmio16d_ao_insn_write\n");
636#endif
637
638	chan = CR_CHAN(insn->chanspec);
639
640	for (i = 0; i < insn->n; i++) {
641		d = data[i];
642		switch (chan) {
643		case 0:
644			if (devpriv->dac0_coding == dac_2comp)
645				d ^= 0x800;
646			outw(d, dev->iobase + DAC0_REG);
647			break;
648		case 1:
649			if (devpriv->dac1_coding == dac_2comp)
650				d ^= 0x800;
651			outw(d, dev->iobase + DAC1_REG);
652			break;
653		default:
654			return -EINVAL;
655		}
656		devpriv->ao_readback[chan] = data[i];
657	}
658	return i;
659}
660
661static int atmio16d_dio_insn_bits(struct comedi_device *dev,
662				  struct comedi_subdevice *s,
663				  struct comedi_insn *insn, unsigned int *data)
664{
665	if (insn->n != 2)
666		return -EINVAL;
667
668	if (data[0]) {
669		s->state &= ~data[0];
670		s->state |= (data[0] | data[1]);
671		outw(s->state, dev->iobase + MIO_16_DIG_OUT_REG);
672	}
673	data[1] = inw(dev->iobase + MIO_16_DIG_IN_REG);
674
675	return 2;
676}
677
678static int atmio16d_dio_insn_config(struct comedi_device *dev,
679				    struct comedi_subdevice *s,
680				    struct comedi_insn *insn,
681				    unsigned int *data)
682{
683	int i;
684	int mask;
685
686	for (i = 0; i < insn->n; i++) {
687		mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
688		s->io_bits &= ~mask;
689		if (data[i])
690			s->io_bits |= mask;
691	}
692	devpriv->com_reg_2_state &= ~(COMREG2_DOUTEN0 | COMREG2_DOUTEN1);
693	if (s->io_bits & 0x0f)
694		devpriv->com_reg_2_state |= COMREG2_DOUTEN0;
695	if (s->io_bits & 0xf0)
696		devpriv->com_reg_2_state |= COMREG2_DOUTEN1;
697	outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
698
699	return i;
700}
701
702/*
703   options[0] - I/O port
704   options[1] - MIO irq
705		0 == no irq
706		N == irq N {3,4,5,6,7,9,10,11,12,14,15}
707   options[2] - DIO irq
708		0 == no irq
709		N == irq N {3,4,5,6,7,9}
710   options[3] - DMA1 channel
711		0 == no DMA
712		N == DMA N {5,6,7}
713   options[4] - DMA2 channel
714		0 == no DMA
715		N == DMA N {5,6,7}
716
717   options[5] - a/d mux
718	0=differential, 1=single
719   options[6] - a/d range
720	0=bipolar10, 1=bipolar5, 2=unipolar10
721
722   options[7] - dac0 range
723	0=bipolar, 1=unipolar
724   options[8] - dac0 reference
725	0=internal, 1=external
726   options[9] - dac0 coding
727	0=2's comp, 1=straight binary
728
729   options[10] - dac1 range
730   options[11] - dac1 reference
731   options[12] - dac1 coding
732 */
733
734static int atmio16d_attach(struct comedi_device *dev,
735			   struct comedi_devconfig *it)
736{
737	unsigned int irq;
738	unsigned long iobase;
739	int ret;
740
741	struct comedi_subdevice *s;
742
743	/* make sure the address range is free and allocate it */
744	iobase = it->options[0];
745	printk(KERN_INFO "comedi%d: atmio16d: 0x%04lx ", dev->minor, iobase);
746	if (!request_region(iobase, ATMIO16D_SIZE, "ni_atmio16d")) {
747		printk("I/O port conflict\n");
748		return -EIO;
749	}
750	dev->iobase = iobase;
751
752	/* board name */
753	dev->board_name = boardtype->name;
754
755	ret = alloc_subdevices(dev, 4);
756	if (ret < 0)
757		return ret;
758
759	ret = alloc_private(dev, sizeof(struct atmio16d_private));
760	if (ret < 0)
761		return ret;
762
763	/* reset the atmio16d hardware */
764	reset_atmio16d(dev);
765
766	/* check if our interrupt is available and get it */
767	irq = it->options[1];
768	if (irq) {
769
770		ret = request_irq(irq, atmio16d_interrupt, 0, "atmio16d", dev);
771		if (ret < 0) {
772			printk(KERN_INFO "failed to allocate irq %u\n", irq);
773			return ret;
774		}
775		dev->irq = irq;
776		printk(KERN_INFO "( irq = %u )\n", irq);
777	} else {
778		printk(KERN_INFO "( no irq )");
779	}
780
781	/* set device options */
782	devpriv->adc_mux = it->options[5];
783	devpriv->adc_range = it->options[6];
784
785	devpriv->dac0_range = it->options[7];
786	devpriv->dac0_reference = it->options[8];
787	devpriv->dac0_coding = it->options[9];
788	devpriv->dac1_range = it->options[10];
789	devpriv->dac1_reference = it->options[11];
790	devpriv->dac1_coding = it->options[12];
791
792	/* setup sub-devices */
793	s = dev->subdevices + 0;
794	dev->read_subdev = s;
795	/* ai subdevice */
796	s->type = COMEDI_SUBD_AI;
797	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
798	s->n_chan = (devpriv->adc_mux ? 16 : 8);
799	s->len_chanlist = 16;
800	s->insn_read = atmio16d_ai_insn_read;
801	s->do_cmdtest = atmio16d_ai_cmdtest;
802	s->do_cmd = atmio16d_ai_cmd;
803	s->cancel = atmio16d_ai_cancel;
804	s->maxdata = 0xfff;	/* 4095 decimal */
805	switch (devpriv->adc_range) {
806	case adc_bipolar10:
807		s->range_table = &range_atmio16d_ai_10_bipolar;
808		break;
809	case adc_bipolar5:
810		s->range_table = &range_atmio16d_ai_5_bipolar;
811		break;
812	case adc_unipolar10:
813		s->range_table = &range_atmio16d_ai_unipolar;
814		break;
815	}
816
817	/* ao subdevice */
818	s++;
819	s->type = COMEDI_SUBD_AO;
820	s->subdev_flags = SDF_WRITABLE;
821	s->n_chan = 2;
822	s->insn_read = atmio16d_ao_insn_read;
823	s->insn_write = atmio16d_ao_insn_write;
824	s->maxdata = 0xfff;	/* 4095 decimal */
825	s->range_table_list = devpriv->ao_range_type_list;
826	switch (devpriv->dac0_range) {
827	case dac_bipolar:
828		devpriv->ao_range_type_list[0] = &range_bipolar10;
829		break;
830	case dac_unipolar:
831		devpriv->ao_range_type_list[0] = &range_unipolar10;
832		break;
833	}
834	switch (devpriv->dac1_range) {
835	case dac_bipolar:
836		devpriv->ao_range_type_list[1] = &range_bipolar10;
837		break;
838	case dac_unipolar:
839		devpriv->ao_range_type_list[1] = &range_unipolar10;
840		break;
841	}
842
843	/* Digital I/O */
844	s++;
845	s->type = COMEDI_SUBD_DIO;
846	s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
847	s->n_chan = 8;
848	s->insn_bits = atmio16d_dio_insn_bits;
849	s->insn_config = atmio16d_dio_insn_config;
850	s->maxdata = 1;
851	s->range_table = &range_digital;
852
853	/* 8255 subdevice */
854	s++;
855	if (boardtype->has_8255)
856		subdev_8255_init(dev, s, NULL, dev->iobase);
857	else
858		s->type = COMEDI_SUBD_UNUSED;
859
860/* don't yet know how to deal with counter/timers */
861#if 0
862	s++;
863	/* do */
864	s->type = COMEDI_SUBD_TIMER;
865	s->n_chan = 0;
866	s->maxdata = 0
867#endif
868	    printk("\n");
869
870	return 0;
871}
872
873static int atmio16d_detach(struct comedi_device *dev)
874{
875	printk(KERN_INFO "comedi%d: atmio16d: remove\n", dev->minor);
876
877	if (dev->subdevices && boardtype->has_8255)
878		subdev_8255_cleanup(dev, dev->subdevices + 3);
879
880	if (dev->irq)
881		free_irq(dev->irq, dev);
882
883	reset_atmio16d(dev);
884
885	if (dev->iobase)
886		release_region(dev->iobase, ATMIO16D_SIZE);
887
888	return 0;
889}
890