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