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