ni_65xx.c revision 0707bb04be89b18ee83b5a997e36cc585f0b988d
1/*
2    comedi/drivers/ni_6514.c
3    driver for National Instruments PCI-6514
4
5    Copyright (C) 2006 Jon Grierson <jd@renko.co.uk>
6    Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net>
7
8    COMEDI - Linux Control and Measurement Device Interface
9    Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
25*/
26/*
27Driver: ni_65xx
28Description: National Instruments 65xx static dio boards
29Author: Jon Grierson <jd@renko.co.uk>, Frank Mori Hess <fmhess@users.sourceforge.net>
30Status: testing
31Devices: [National Instruments] PCI-6509 (ni_65xx), PXI-6509, PCI-6510, PCI-6511,
32  PXI-6511, PCI-6512, PXI-6512, PCI-6513, PXI-6513, PCI-6514, PXI-6514, PCI-6515,
33  PXI-6515, PCI-6516, PCI-6517, PCI-6518, PCI-6519, PCI-6520, PCI-6521, PXI-6521,
34  PCI-6528, PXI-6528
35Updated: Wed Oct 18 08:59:11 EDT 2006
36
37Based on the PCI-6527 driver by ds.
38The interrupt subdevice (subdevice 3) is probably broken for all boards
39except maybe the 6514.
40
41*/
42
43/*
44   Manuals (available from ftp://ftp.natinst.com/support/manuals)
45
46	370106b.pdf	6514 Register Level Programmer Manual
47
48 */
49
50#define _GNU_SOURCE
51#define DEBUG 1
52#define DEBUG_FLAGS
53#include "../comedidev.h"
54
55#include "mite.h"
56
57#define NI6514_DIO_SIZE 4096
58#define NI6514_MITE_SIZE 4096
59
60#define NI_65XX_MAX_NUM_PORTS 12
61static const unsigned ni_65xx_channels_per_port = 8;
62static const unsigned ni_65xx_port_offset = 0x10;
63
64static inline unsigned Port_Data(unsigned port)
65{
66	return 0x40 + port * ni_65xx_port_offset;
67}
68static inline unsigned Port_Select(unsigned port)
69{
70	return 0x41 + port * ni_65xx_port_offset;
71}
72static inline unsigned Rising_Edge_Detection_Enable(unsigned port)
73{
74	return 0x42 + port * ni_65xx_port_offset;
75}
76static inline unsigned Falling_Edge_Detection_Enable(unsigned port)
77{
78	return 0x43 + port * ni_65xx_port_offset;
79}
80static inline unsigned Filter_Enable(unsigned port)
81{
82	return 0x44 + port * ni_65xx_port_offset;
83}
84
85#define ID_Register				0x00
86
87#define Clear_Register				0x01
88#define ClrEdge				0x08
89#define ClrOverflow			0x04
90
91#define Filter_Interval			0x08
92
93#define Change_Status				0x02
94#define MasterInterruptStatus		0x04
95#define Overflow			0x02
96#define EdgeStatus			0x01
97
98#define Master_Interrupt_Control		0x03
99#define FallingEdgeIntEnable		0x10
100#define RisingEdgeIntEnable		0x08
101#define MasterInterruptEnable		0x04
102#define OverflowIntEnable		0x02
103#define EdgeIntEnable			0x01
104
105static int ni_65xx_attach(struct comedi_device * dev, struct comedi_devconfig * it);
106static int ni_65xx_detach(struct comedi_device * dev);
107static struct comedi_driver driver_ni_65xx = {
108      driver_name:"ni_65xx",
109      module:THIS_MODULE,
110      attach:ni_65xx_attach,
111      detach:ni_65xx_detach,
112};
113
114typedef struct {
115	int dev_id;
116	const char *name;
117	unsigned num_dio_ports;
118	unsigned num_di_ports;
119	unsigned num_do_ports;
120	unsigned invert_outputs:1;
121} ni_65xx_board;
122static const ni_65xx_board ni_65xx_boards[] = {
123	{
124	      dev_id:	0x7085,
125	      name:	"pci-6509",
126	      num_dio_ports:12,
127      invert_outputs:0},
128	{
129	      dev_id:	0x1710,
130	      name:	"pxi-6509",
131	      num_dio_ports:12,
132      invert_outputs:0},
133	{
134	      dev_id:	0x7124,
135	      name:	"pci-6510",
136      num_di_ports:4},
137	{
138	      dev_id:	0x70c3,
139	      name:	"pci-6511",
140      num_di_ports:8},
141	{
142	      dev_id:	0x70d3,
143	      name:	"pxi-6511",
144      num_di_ports:8},
145	{
146	      dev_id:	0x70cc,
147	      name:	"pci-6512",
148      num_do_ports:8},
149	{
150	      dev_id:	0x70d2,
151	      name:	"pxi-6512",
152      num_do_ports:8},
153	{
154	      dev_id:	0x70c8,
155	      name:	"pci-6513",
156	      num_do_ports:8,
157      invert_outputs:1},
158	{
159	      dev_id:	0x70d1,
160	      name:	"pxi-6513",
161	      num_do_ports:8,
162      invert_outputs:1},
163	{
164	      dev_id:	0x7088,
165	      name:	"pci-6514",
166	      num_di_ports:4,
167	      num_do_ports:4,
168      invert_outputs:1},
169	{
170	      dev_id:	0x70CD,
171	      name:	"pxi-6514",
172	      num_di_ports:4,
173	      num_do_ports:4,
174      invert_outputs:1},
175	{
176	      dev_id:	0x7087,
177	      name:	"pci-6515",
178	      num_di_ports:4,
179	      num_do_ports:4,
180      invert_outputs:1},
181	{
182	      dev_id:	0x70c9,
183	      name:	"pxi-6515",
184	      num_di_ports:4,
185	      num_do_ports:4,
186      invert_outputs:1},
187	{
188	      dev_id:	0x7125,
189	      name:	"pci-6516",
190	      num_do_ports:4,
191      invert_outputs:1},
192	{
193	      dev_id:	0x7126,
194	      name:	"pci-6517",
195	      num_do_ports:4,
196      invert_outputs:1},
197	{
198	      dev_id:	0x7127,
199	      name:	"pci-6518",
200	      num_di_ports:2,
201	      num_do_ports:2,
202      invert_outputs:1},
203	{
204	      dev_id:	0x7128,
205	      name:	"pci-6519",
206	      num_di_ports:2,
207	      num_do_ports:2,
208      invert_outputs:1},
209	{
210	      dev_id:	0x71c5,
211	      name:	"pci-6520",
212	      num_di_ports:1,
213	      num_do_ports:1,
214		},
215	{
216	      dev_id:	0x718b,
217	      name:	"pci-6521",
218	      num_di_ports:1,
219	      num_do_ports:1,
220		},
221	{
222	      dev_id:	0x718c,
223	      name:	"pxi-6521",
224	      num_di_ports:1,
225	      num_do_ports:1,
226		},
227	{
228	      dev_id:	0x70a9,
229	      name:	"pci-6528",
230	      num_di_ports:3,
231	      num_do_ports:3,
232		},
233	{
234	      dev_id:	0x7086,
235	      name:	"pxi-6528",
236	      num_di_ports:3,
237	      num_do_ports:3,
238		},
239};
240
241#define n_ni_65xx_boards (sizeof(ni_65xx_boards)/sizeof(ni_65xx_boards[0]))
242static inline const ni_65xx_board *board(struct comedi_device * dev)
243{
244	return dev->board_ptr;
245}
246static inline unsigned ni_65xx_port_by_channel(unsigned channel)
247{
248	return channel / ni_65xx_channels_per_port;
249}
250static inline unsigned ni_65xx_total_num_ports(const ni_65xx_board * board)
251{
252	return board->num_dio_ports + board->num_di_ports + board->num_do_ports;
253}
254
255static DEFINE_PCI_DEVICE_TABLE(ni_65xx_pci_table) = {
256	{PCI_VENDOR_ID_NATINST, 0x1710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
257	{PCI_VENDOR_ID_NATINST, 0x7085, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
258	{PCI_VENDOR_ID_NATINST, 0x7086, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
259	{PCI_VENDOR_ID_NATINST, 0x7087, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
260	{PCI_VENDOR_ID_NATINST, 0x7088, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
261	{PCI_VENDOR_ID_NATINST, 0x70a9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
262	{PCI_VENDOR_ID_NATINST, 0x70c3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
263	{PCI_VENDOR_ID_NATINST, 0x70c8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
264	{PCI_VENDOR_ID_NATINST, 0x70c9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
265	{PCI_VENDOR_ID_NATINST, 0x70cc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
266	{PCI_VENDOR_ID_NATINST, 0x70CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
267	{PCI_VENDOR_ID_NATINST, 0x70d1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
268	{PCI_VENDOR_ID_NATINST, 0x70d2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
269	{PCI_VENDOR_ID_NATINST, 0x70d3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
270	{PCI_VENDOR_ID_NATINST, 0x7124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
271	{PCI_VENDOR_ID_NATINST, 0x7125, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
272	{PCI_VENDOR_ID_NATINST, 0x7126, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
273	{PCI_VENDOR_ID_NATINST, 0x7127, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
274	{PCI_VENDOR_ID_NATINST, 0x7128, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
275	{PCI_VENDOR_ID_NATINST, 0x718b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
276	{PCI_VENDOR_ID_NATINST, 0x718c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
277	{PCI_VENDOR_ID_NATINST, 0x71c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
278	{0}
279};
280
281MODULE_DEVICE_TABLE(pci, ni_65xx_pci_table);
282
283typedef struct {
284	struct mite_struct *mite;
285	unsigned int filter_interval;
286	unsigned short filter_enable[NI_65XX_MAX_NUM_PORTS];
287	unsigned short output_bits[NI_65XX_MAX_NUM_PORTS];
288	unsigned short dio_direction[NI_65XX_MAX_NUM_PORTS];
289} ni_65xx_private;
290static inline ni_65xx_private *private(struct comedi_device * dev)
291{
292	return dev->private;
293}
294
295typedef struct {
296	unsigned base_port;
297} ni_65xx_subdevice_private;
298static inline ni_65xx_subdevice_private *sprivate(struct comedi_subdevice * subdev)
299{
300	return subdev->private;
301}
302static ni_65xx_subdevice_private *ni_65xx_alloc_subdevice_private(void)
303{
304	ni_65xx_subdevice_private *subdev_private =
305		kzalloc(sizeof(ni_65xx_subdevice_private), GFP_KERNEL);
306	if (subdev_private == NULL)
307		return NULL;
308	return subdev_private;
309}
310
311static int ni_65xx_find_device(struct comedi_device * dev, int bus, int slot);
312
313static int ni_65xx_config_filter(struct comedi_device * dev, struct comedi_subdevice * s,
314	struct comedi_insn * insn, unsigned int * data)
315{
316	const unsigned chan = CR_CHAN(insn->chanspec);
317	const unsigned port =
318		sprivate(s)->base_port + ni_65xx_port_by_channel(chan);
319
320	if (data[0] != INSN_CONFIG_FILTER)
321		return -EINVAL;
322	if (data[1]) {
323		static const unsigned filter_resolution_ns = 200;
324		static const unsigned max_filter_interval = 0xfffff;
325		unsigned interval =
326			(data[1] +
327			(filter_resolution_ns / 2)) / filter_resolution_ns;
328		if (interval > max_filter_interval)
329			interval = max_filter_interval;
330		data[1] = interval * filter_resolution_ns;
331
332		if (interval != private(dev)->filter_interval) {
333			writeb(interval,
334				private(dev)->mite->daq_io_addr +
335				Filter_Interval);
336			private(dev)->filter_interval = interval;
337		}
338
339		private(dev)->filter_enable[port] |=
340			1 << (chan % ni_65xx_channels_per_port);
341	} else {
342		private(dev)->filter_enable[port] &=
343			~(1 << (chan % ni_65xx_channels_per_port));
344	}
345
346	writeb(private(dev)->filter_enable[port],
347		private(dev)->mite->daq_io_addr + Filter_Enable(port));
348
349	return 2;
350}
351
352static int ni_65xx_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
353	struct comedi_insn * insn, unsigned int * data)
354{
355	unsigned port;
356
357	if (insn->n < 1)
358		return -EINVAL;
359	port = sprivate(s)->base_port +
360		ni_65xx_port_by_channel(CR_CHAN(insn->chanspec));
361	switch (data[0]) {
362	case INSN_CONFIG_FILTER:
363		return ni_65xx_config_filter(dev, s, insn, data);
364		break;
365	case INSN_CONFIG_DIO_OUTPUT:
366		if (s->type != COMEDI_SUBD_DIO)
367			return -EINVAL;
368		private(dev)->dio_direction[port] = COMEDI_OUTPUT;
369		writeb(0, private(dev)->mite->daq_io_addr + Port_Select(port));
370		return 1;
371		break;
372	case INSN_CONFIG_DIO_INPUT:
373		if (s->type != COMEDI_SUBD_DIO)
374			return -EINVAL;
375		private(dev)->dio_direction[port] = COMEDI_INPUT;
376		writeb(1, private(dev)->mite->daq_io_addr + Port_Select(port));
377		return 1;
378		break;
379	case INSN_CONFIG_DIO_QUERY:
380		if (s->type != COMEDI_SUBD_DIO)
381			return -EINVAL;
382		data[1] = private(dev)->dio_direction[port];
383		return insn->n;
384		break;
385	default:
386		break;
387	}
388	return -EINVAL;
389}
390
391static int ni_65xx_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
392	struct comedi_insn * insn, unsigned int * data)
393{
394	unsigned base_bitfield_channel;
395	const unsigned max_ports_per_bitfield = 5;
396	unsigned read_bits = 0;
397	unsigned j;
398	if (insn->n != 2)
399		return -EINVAL;
400	base_bitfield_channel = CR_CHAN(insn->chanspec);
401	for (j = 0; j < max_ports_per_bitfield; ++j) {
402		const unsigned port =
403			sprivate(s)->base_port +
404			ni_65xx_port_by_channel(base_bitfield_channel) + j;
405		unsigned base_port_channel;
406		unsigned port_mask, port_data, port_read_bits;
407		int bitshift;
408		if (port >= ni_65xx_total_num_ports(board(dev)))
409			break;
410		base_port_channel = port * ni_65xx_channels_per_port;
411		port_mask = data[0];
412		port_data = data[1];
413		bitshift = base_port_channel - base_bitfield_channel;
414		if (bitshift >= 32 || bitshift <= -32)
415			break;
416		if (bitshift > 0) {
417			port_mask >>= bitshift;
418			port_data >>= bitshift;
419		} else {
420			port_mask <<= -bitshift;
421			port_data <<= -bitshift;
422		}
423		port_mask &= 0xff;
424		port_data &= 0xff;
425		if (port_mask) {
426			unsigned bits;
427			private(dev)->output_bits[port] &= ~port_mask;
428			private(dev)->output_bits[port] |=
429				port_data & port_mask;
430			bits = private(dev)->output_bits[port];
431			if (board(dev)->invert_outputs)
432				bits = ~bits;
433			writeb(bits,
434				private(dev)->mite->daq_io_addr +
435				Port_Data(port));
436//                      rt_printk("wrote 0x%x to port %i\n", bits, port);
437		}
438		port_read_bits =
439			readb(private(dev)->mite->daq_io_addr +
440			Port_Data(port));
441//              rt_printk("read 0x%x from port %i\n", port_read_bits, port);
442		if (bitshift > 0) {
443			port_read_bits <<= bitshift;
444		} else {
445			port_read_bits >>= -bitshift;
446		}
447		read_bits |= port_read_bits;
448	}
449	data[1] = read_bits;
450	return insn->n;
451}
452
453static irqreturn_t ni_65xx_interrupt(int irq, void *d PT_REGS_ARG)
454{
455	struct comedi_device *dev = d;
456	struct comedi_subdevice *s = dev->subdevices + 2;
457	unsigned int status;
458
459	status = readb(private(dev)->mite->daq_io_addr + Change_Status);
460	if ((status & MasterInterruptStatus) == 0)
461		return IRQ_NONE;
462	if ((status & EdgeStatus) == 0)
463		return IRQ_NONE;
464
465	writeb(ClrEdge | ClrOverflow,
466		private(dev)->mite->daq_io_addr + Clear_Register);
467
468	comedi_buf_put(s->async, 0);
469	s->async->events |= COMEDI_CB_EOS;
470	comedi_event(dev, s);
471	return IRQ_HANDLED;
472}
473
474static int ni_65xx_intr_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
475	struct comedi_cmd * cmd)
476{
477	int err = 0;
478	int tmp;
479
480	/* step 1: make sure trigger sources are trivially valid */
481
482	tmp = cmd->start_src;
483	cmd->start_src &= TRIG_NOW;
484	if (!cmd->start_src || tmp != cmd->start_src)
485		err++;
486
487	tmp = cmd->scan_begin_src;
488	cmd->scan_begin_src &= TRIG_OTHER;
489	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
490		err++;
491
492	tmp = cmd->convert_src;
493	cmd->convert_src &= TRIG_FOLLOW;
494	if (!cmd->convert_src || tmp != cmd->convert_src)
495		err++;
496
497	tmp = cmd->scan_end_src;
498	cmd->scan_end_src &= TRIG_COUNT;
499	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
500		err++;
501
502	tmp = cmd->stop_src;
503	cmd->stop_src &= TRIG_COUNT;
504	if (!cmd->stop_src || tmp != cmd->stop_src)
505		err++;
506
507	if (err)
508		return 1;
509
510	/* step 2: make sure trigger sources are unique and mutually compatible */
511
512	if (err)
513		return 2;
514
515	/* step 3: make sure arguments are trivially compatible */
516
517	if (cmd->start_arg != 0) {
518		cmd->start_arg = 0;
519		err++;
520	}
521	if (cmd->scan_begin_arg != 0) {
522		cmd->scan_begin_arg = 0;
523		err++;
524	}
525	if (cmd->convert_arg != 0) {
526		cmd->convert_arg = 0;
527		err++;
528	}
529
530	if (cmd->scan_end_arg != 1) {
531		cmd->scan_end_arg = 1;
532		err++;
533	}
534	if (cmd->stop_arg != 0) {
535		cmd->stop_arg = 0;
536		err++;
537	}
538
539	if (err)
540		return 3;
541
542	/* step 4: fix up any arguments */
543
544	if (err)
545		return 4;
546
547	return 0;
548}
549
550static int ni_65xx_intr_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
551{
552	//struct comedi_cmd *cmd = &s->async->cmd;
553
554	writeb(ClrEdge | ClrOverflow,
555		private(dev)->mite->daq_io_addr + Clear_Register);
556	writeb(FallingEdgeIntEnable | RisingEdgeIntEnable |
557		MasterInterruptEnable | EdgeIntEnable,
558		private(dev)->mite->daq_io_addr + Master_Interrupt_Control);
559
560	return 0;
561}
562
563static int ni_65xx_intr_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
564{
565	writeb(0x00,
566		private(dev)->mite->daq_io_addr + Master_Interrupt_Control);
567
568	return 0;
569}
570
571static int ni_65xx_intr_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
572	struct comedi_insn * insn, unsigned int * data)
573{
574	if (insn->n < 1)
575		return -EINVAL;
576
577	data[1] = 0;
578	return 2;
579}
580
581static int ni_65xx_intr_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
582	struct comedi_insn * insn, unsigned int * data)
583{
584	if (insn->n < 1)
585		return -EINVAL;
586	if (data[0] != INSN_CONFIG_CHANGE_NOTIFY)
587		return -EINVAL;
588
589	writeb(data[1],
590		private(dev)->mite->daq_io_addr +
591		Rising_Edge_Detection_Enable(0));
592	writeb(data[1] >> 8,
593		private(dev)->mite->daq_io_addr +
594		Rising_Edge_Detection_Enable(0x10));
595	writeb(data[1] >> 16,
596		private(dev)->mite->daq_io_addr +
597		Rising_Edge_Detection_Enable(0x20));
598	writeb(data[1] >> 24,
599		private(dev)->mite->daq_io_addr +
600		Rising_Edge_Detection_Enable(0x30));
601
602	writeb(data[2],
603		private(dev)->mite->daq_io_addr +
604		Falling_Edge_Detection_Enable(0));
605	writeb(data[2] >> 8,
606		private(dev)->mite->daq_io_addr +
607		Falling_Edge_Detection_Enable(0x10));
608	writeb(data[2] >> 16,
609		private(dev)->mite->daq_io_addr +
610		Falling_Edge_Detection_Enable(0x20));
611	writeb(data[2] >> 24,
612		private(dev)->mite->daq_io_addr +
613		Falling_Edge_Detection_Enable(0x30));
614
615	return 2;
616}
617
618static int ni_65xx_attach(struct comedi_device * dev, struct comedi_devconfig * it)
619{
620	struct comedi_subdevice *s;
621	unsigned i;
622	int ret;
623
624	printk("comedi%d: ni_65xx:", dev->minor);
625
626	if ((ret = alloc_private(dev, sizeof(ni_65xx_private))) < 0)
627		return ret;
628
629	ret = ni_65xx_find_device(dev, it->options[0], it->options[1]);
630	if (ret < 0)
631		return ret;
632
633	ret = mite_setup(private(dev)->mite);
634	if (ret < 0) {
635		printk("error setting up mite\n");
636		return ret;
637	}
638
639	dev->board_name = board(dev)->name;
640	dev->irq = mite_irq(private(dev)->mite);
641	printk(" %s", dev->board_name);
642
643	printk(" ID=0x%02x",
644		readb(private(dev)->mite->daq_io_addr + ID_Register));
645
646	if ((ret = alloc_subdevices(dev, 4)) < 0)
647		return ret;
648
649	s = dev->subdevices + 0;
650	if (board(dev)->num_di_ports) {
651		s->type = COMEDI_SUBD_DI;
652		s->subdev_flags = SDF_READABLE;
653		s->n_chan =
654			board(dev)->num_di_ports * ni_65xx_channels_per_port;
655		s->range_table = &range_digital;
656		s->maxdata = 1;
657		s->insn_config = ni_65xx_dio_insn_config;
658		s->insn_bits = ni_65xx_dio_insn_bits;
659		s->private = ni_65xx_alloc_subdevice_private();
660		if (s->private == NULL)
661			return -ENOMEM;
662		sprivate(s)->base_port = 0;
663	} else {
664		s->type = COMEDI_SUBD_UNUSED;
665	}
666
667	s = dev->subdevices + 1;
668	if (board(dev)->num_do_ports) {
669		s->type = COMEDI_SUBD_DO;
670		s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
671		s->n_chan =
672			board(dev)->num_do_ports * ni_65xx_channels_per_port;
673		s->range_table = &range_digital;
674		s->maxdata = 1;
675		s->insn_bits = ni_65xx_dio_insn_bits;
676		s->private = ni_65xx_alloc_subdevice_private();
677		if (s->private == NULL)
678			return -ENOMEM;
679		sprivate(s)->base_port = board(dev)->num_di_ports;
680	} else {
681		s->type = COMEDI_SUBD_UNUSED;
682	}
683
684	s = dev->subdevices + 2;
685	if (board(dev)->num_dio_ports) {
686		s->type = COMEDI_SUBD_DIO;
687		s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
688		s->n_chan =
689			board(dev)->num_dio_ports * ni_65xx_channels_per_port;
690		s->range_table = &range_digital;
691		s->maxdata = 1;
692		s->insn_config = ni_65xx_dio_insn_config;
693		s->insn_bits = ni_65xx_dio_insn_bits;
694		s->private = ni_65xx_alloc_subdevice_private();
695		if (s->private == NULL)
696			return -ENOMEM;
697		sprivate(s)->base_port = 0;
698		for (i = 0; i < board(dev)->num_dio_ports; ++i) {
699			// configure all ports for input
700			writeb(0x1,
701				private(dev)->mite->daq_io_addr +
702				Port_Select(i));
703		}
704	} else {
705		s->type = COMEDI_SUBD_UNUSED;
706	}
707
708	s = dev->subdevices + 3;
709	dev->read_subdev = s;
710	s->type = COMEDI_SUBD_DI;
711	s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
712	s->n_chan = 1;
713	s->range_table = &range_unknown;
714	s->maxdata = 1;
715	s->do_cmdtest = ni_65xx_intr_cmdtest;
716	s->do_cmd = ni_65xx_intr_cmd;
717	s->cancel = ni_65xx_intr_cancel;
718	s->insn_bits = ni_65xx_intr_insn_bits;
719	s->insn_config = ni_65xx_intr_insn_config;
720
721	for (i = 0; i < ni_65xx_total_num_ports(board(dev)); ++i) {
722		writeb(0x00,
723			private(dev)->mite->daq_io_addr + Filter_Enable(i));
724		if (board(dev)->invert_outputs)
725			writeb(0x01,
726				private(dev)->mite->daq_io_addr + Port_Data(i));
727		else
728			writeb(0x00,
729				private(dev)->mite->daq_io_addr + Port_Data(i));
730	}
731	writeb(ClrEdge | ClrOverflow,
732		private(dev)->mite->daq_io_addr + Clear_Register);
733	writeb(0x00,
734		private(dev)->mite->daq_io_addr + Master_Interrupt_Control);
735
736	/* Set filter interval to 0  (32bit reg) */
737	writeb(0x00000000, private(dev)->mite->daq_io_addr + Filter_Interval);
738
739	ret = comedi_request_irq(dev->irq, ni_65xx_interrupt, IRQF_SHARED,
740		"ni_65xx", dev);
741	if (ret < 0) {
742		dev->irq = 0;
743		printk(" irq not available");
744	}
745
746	printk("\n");
747
748	return 0;
749}
750
751static int ni_65xx_detach(struct comedi_device * dev)
752{
753	if (private(dev) && private(dev)->mite
754		&& private(dev)->mite->daq_io_addr) {
755		writeb(0x00,
756			private(dev)->mite->daq_io_addr +
757			Master_Interrupt_Control);
758	}
759
760	if (dev->irq) {
761		comedi_free_irq(dev->irq, dev);
762	}
763
764	if (private(dev)) {
765		unsigned i;
766		for (i = 0; i < dev->n_subdevices; ++i) {
767			if (dev->subdevices[i].private) {
768				kfree(dev->subdevices[i].private);
769				dev->subdevices[i].private = NULL;
770			}
771		}
772		if (private(dev)->mite) {
773			mite_unsetup(private(dev)->mite);
774		}
775	}
776	return 0;
777}
778
779static int ni_65xx_find_device(struct comedi_device * dev, int bus, int slot)
780{
781	struct mite_struct *mite;
782	int i;
783
784	for (mite = mite_devices; mite; mite = mite->next) {
785		if (mite->used)
786			continue;
787		if (bus || slot) {
788			if (bus != mite->pcidev->bus->number ||
789				slot != PCI_SLOT(mite->pcidev->devfn))
790				continue;
791		}
792		for (i = 0; i < n_ni_65xx_boards; i++) {
793			if (mite_device_id(mite) == ni_65xx_boards[i].dev_id) {
794				dev->board_ptr = ni_65xx_boards + i;
795				private(dev)->mite = mite;
796				return 0;
797			}
798		}
799	}
800	printk("no device found\n");
801	mite_list_devices();
802	return -EIO;
803}
804
805COMEDI_PCI_INITCLEANUP(driver_ni_65xx, ni_65xx_pci_table);
806