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