ni_mio_cs.c revision 4fecf4a2a64c453ea8e57c166edb5385082e3948
1/*
2    comedi/drivers/ni_mio_cs.c
3    Hardware driver for NI PCMCIA MIO E series cards
4
5    COMEDI - Linux Control and Measurement Device Interface
6    Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23/*
24Driver: ni_mio_cs
25Description: National Instruments DAQCard E series
26Author: ds
27Status: works
28Devices: [National Instruments] DAQCard-AI-16XE-50 (ni_mio_cs),
29  DAQCard-AI-16E-4, DAQCard-6062E, DAQCard-6024E, DAQCard-6036E
30Updated: Thu Oct 23 19:43:17 CDT 2003
31
32See the notes in the ni_atmio.o driver.
33*/
34/*
35	The real guts of the driver is in ni_mio_common.c, which is
36	included by all the E series drivers.
37
38	References for specifications:
39
40	   341080a.pdf  DAQCard E Series Register Level Programmer Manual
41
42*/
43
44#include "../comedidev.h"
45
46#include <linux/delay.h>
47#include <linux/version.h>
48
49#include "ni_stc.h"
50#include "8255.h"
51
52#include <pcmcia/cs_types.h>
53#include <pcmcia/cs.h>
54#include <pcmcia/cistpl.h>
55#include <pcmcia/ds.h>
56
57#undef DEBUG
58
59#define ATMIO 1
60#undef PCIMIO
61
62/*
63 *  AT specific setup
64 */
65
66#define NI_SIZE 0x20
67
68#define MAX_N_CALDACS 32
69
70static const ni_board ni_boards[] = {
71      {device_id:0x010d,
72	      name:	"DAQCard-ai-16xe-50",
73	      n_adchan:16,
74	      adbits:	16,
75	      ai_fifo_depth:1024,
76	      alwaysdither:0,
77	      gainlkup:ai_gain_8,
78	      ai_speed:5000,
79	      n_aochan:0,
80	      aobits:	0,
81	      ao_fifo_depth:0,
82	      ao_unipolar:0,
83	      num_p0_dio_channels:8,
84	      has_8255:0,
85	      caldac:	{dac8800, dac8043},
86		},
87      {device_id:0x010c,
88	      name:	"DAQCard-ai-16e-4",
89	      n_adchan:16,
90	      adbits:	12,
91	      ai_fifo_depth:1024,
92	      alwaysdither:0,
93	      gainlkup:ai_gain_16,
94	      ai_speed:4000,
95	      n_aochan:0,
96	      aobits:	0,
97	      ao_fifo_depth:0,
98	      ao_unipolar:0,
99	      num_p0_dio_channels:8,
100	      has_8255:0,
101	      caldac:	{mb88341},	/* verified */
102		},
103      {device_id:0x02c4,
104	      name:	"DAQCard-6062E",
105	      n_adchan:16,
106	      adbits:	12,
107	      ai_fifo_depth:8192,
108	      alwaysdither:0,
109	      gainlkup:ai_gain_16,
110	      ai_speed:2000,
111	      n_aochan:2,
112	      aobits:	12,
113	      ao_fifo_depth:2048,
114	      ao_range_table:&range_bipolar10,
115	      ao_unipolar:0,
116	      ao_speed:1176,
117	      num_p0_dio_channels:8,
118	      has_8255:0,
119	      caldac:	{ad8804_debug},	/* verified */
120		},
121      {device_id:0x075e,
122	      name:	"DAQCard-6024E",	/* specs incorrect! */
123	      n_adchan:16,
124	      adbits:	12,
125	      ai_fifo_depth:1024,
126	      alwaysdither:0,
127	      gainlkup:ai_gain_16,
128	      ai_speed:5000,
129	      n_aochan:2,
130	      aobits:	12,
131	      ao_fifo_depth:0,
132	      ao_range_table:&range_bipolar10,
133	      ao_unipolar:0,
134	      ao_speed:1000000,
135	      num_p0_dio_channels:8,
136	      has_8255:0,
137	      caldac:	{ad8804_debug},
138		},
139      {device_id:0x0245,
140	      name:	"DAQCard-6036E",	/* specs incorrect! */
141	      n_adchan:16,
142	      adbits:	16,
143	      ai_fifo_depth:1024,
144	      alwaysdither:1,
145	      gainlkup:ai_gain_4,
146	      ai_speed:5000,
147	      n_aochan:2,
148	      aobits:	16,
149	      ao_fifo_depth:0,
150	      ao_range_table:&range_bipolar10,
151	      ao_unipolar:0,
152	      ao_speed:1000000,
153	      num_p0_dio_channels:8,
154	      has_8255:0,
155	      caldac:	{ad8804_debug},
156		},
157#if 0
158      {device_id:0x0000,	/* unknown */
159	      name:	"DAQCard-6715",
160	      n_adchan:0,
161	      n_aochan:8,
162	      aobits:	12,
163	      ao_671x:	8192,
164	      num_p0_dio_channels:8,
165	      caldac:	{mb88341, mb88341},
166		},
167#endif
168	/* N.B. Update ni_mio_cs_ids[] when entries added above. */
169};
170
171#define interrupt_pin(a)	0
172
173#define IRQ_POLARITY 1
174
175#define NI_E_IRQ_FLAGS		IRQF_SHARED
176
177typedef struct {
178	struct pcmcia_device *link;
179
180 NI_PRIVATE_COMMON} ni_private;
181#define devpriv ((ni_private *)dev->private)
182
183/* How we access registers */
184
185#define ni_writel(a,b)		(outl((a),(b)+dev->iobase))
186#define ni_readl(a)		(inl((a)+dev->iobase))
187#define ni_writew(a,b)		(outw((a),(b)+dev->iobase))
188#define ni_readw(a)		(inw((a)+dev->iobase))
189#define ni_writeb(a,b)		(outb((a),(b)+dev->iobase))
190#define ni_readb(a)		(inb((a)+dev->iobase))
191
192/* How we access windowed registers */
193
194/* We automatically take advantage of STC registers that can be
195 * read/written directly in the I/O space of the board.  The
196 * DAQCard devices map the low 8 STC registers to iobase+addr*2. */
197
198static void mio_cs_win_out(comedi_device * dev, uint16_t data, int addr)
199{
200	unsigned long flags;
201
202	comedi_spin_lock_irqsave(&devpriv->window_lock, flags);
203	if (addr < 8) {
204		ni_writew(data, addr * 2);
205	} else {
206		ni_writew(addr, Window_Address);
207		ni_writew(data, Window_Data);
208	}
209	comedi_spin_unlock_irqrestore(&devpriv->window_lock, flags);
210}
211
212static uint16_t mio_cs_win_in(comedi_device * dev, int addr)
213{
214	unsigned long flags;
215	uint16_t ret;
216
217	comedi_spin_lock_irqsave(&devpriv->window_lock, flags);
218	if (addr < 8) {
219		ret = ni_readw(addr * 2);
220	} else {
221		ni_writew(addr, Window_Address);
222		ret = ni_readw(Window_Data);
223	}
224	comedi_spin_unlock_irqrestore(&devpriv->window_lock, flags);
225
226	return ret;
227}
228
229static int mio_cs_attach(comedi_device * dev, comedi_devconfig * it);
230static int mio_cs_detach(comedi_device * dev);
231static comedi_driver driver_ni_mio_cs = {
232      driver_name:"ni_mio_cs",
233      module:THIS_MODULE,
234      attach:mio_cs_attach,
235      detach:mio_cs_detach,
236};
237
238#include "ni_mio_common.c"
239
240static int ni_getboardtype(comedi_device * dev, struct pcmcia_device *link);
241
242/* clean up allocated resources */
243/* called when driver is removed */
244static int mio_cs_detach(comedi_device * dev)
245{
246	mio_common_detach(dev);
247
248	/* PCMCIA layer frees the IO region */
249
250	if (dev->irq) {
251		comedi_free_irq(dev->irq, dev);
252	}
253
254	return 0;
255}
256
257static void mio_cs_config(struct pcmcia_device *link);
258static void cs_release(struct pcmcia_device *link);
259static void cs_detach(struct pcmcia_device *);
260
261static struct pcmcia_device *cur_dev = NULL;
262static const dev_info_t dev_info = "ni_mio_cs";
263static dev_node_t dev_node = {
264	"ni_mio_cs",
265	COMEDI_MAJOR, 0,
266	NULL
267};
268static int cs_attach(struct pcmcia_device *link)
269{
270	link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
271	link->io.NumPorts1 = 16;
272	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
273	link->irq.IRQInfo1 = IRQ_LEVEL_ID;
274	link->conf.Attributes = CONF_ENABLE_IRQ;
275	link->conf.IntType = INT_MEMORY_AND_IO;
276
277	cur_dev = link;
278
279	mio_cs_config(link);
280
281	return 0;
282}
283
284static void cs_release(struct pcmcia_device *link)
285{
286	pcmcia_disable_device(link);
287}
288
289static void cs_detach(struct pcmcia_device *link)
290{
291	DPRINTK("cs_detach(link=%p)\n", link);
292
293	if (link->dev_node) {
294		cs_release(link);
295	}
296}
297
298static int mio_cs_suspend(struct pcmcia_device *link)
299{
300	DPRINTK("pm suspend\n");
301
302	return 0;
303}
304
305static int mio_cs_resume(struct pcmcia_device *link)
306{
307	DPRINTK("pm resume\n");
308	return 0;
309}
310
311static void mio_cs_config(struct pcmcia_device *link)
312{
313	tuple_t tuple;
314	u_short buf[128];
315	cisparse_t parse;
316	int manfid = 0, prodid = 0;
317	int ret;
318
319	DPRINTK("mio_cs_config(link=%p)\n", link);
320
321	tuple.TupleData = (cisdata_t *) buf;
322	tuple.TupleOffset = 0;
323	tuple.TupleDataMax = 255;
324	tuple.Attributes = 0;
325
326	tuple.DesiredTuple = CISTPL_CONFIG;
327	ret = pcmcia_get_first_tuple(link, &tuple);
328	ret = pcmcia_get_tuple_data(link, &tuple);
329	ret = pcmcia_parse_tuple(&tuple, &parse);
330	link->conf.ConfigBase = parse.config.base;
331	link->conf.Present = parse.config.rmask[0];
332
333#if 0
334	tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
335	tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
336	info->multi(first_tuple(link, &tuple, &parse) == 0);
337#endif
338
339	tuple.DesiredTuple = CISTPL_MANFID;
340	tuple.Attributes = TUPLE_RETURN_COMMON;
341	if ((pcmcia_get_first_tuple(link, &tuple) == 0) &&
342		(pcmcia_get_tuple_data(link, &tuple) == 0)) {
343		manfid = le16_to_cpu(buf[0]);
344		prodid = le16_to_cpu(buf[1]);
345	}
346	//printk("manfid = 0x%04x, 0x%04x\n",manfid,prodid);
347
348	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
349	tuple.Attributes = 0;
350	ret = pcmcia_get_first_tuple(link, &tuple);
351	ret = pcmcia_get_tuple_data(link, &tuple);
352	ret = pcmcia_parse_tuple(&tuple, &parse);
353
354#if 0
355	printk(" index: 0x%x\n", parse.cftable_entry.index);
356	printk(" flags: 0x%x\n", parse.cftable_entry.flags);
357	printk(" io flags: 0x%x\n", parse.cftable_entry.io.flags);
358	printk(" io nwin: 0x%x\n", parse.cftable_entry.io.nwin);
359	printk(" io base: 0x%x\n", parse.cftable_entry.io.win[0].base);
360	printk(" io len: 0x%x\n", parse.cftable_entry.io.win[0].len);
361	printk(" irq1: 0x%x\n", parse.cftable_entry.irq.IRQInfo1);
362	printk(" irq2: 0x%x\n", parse.cftable_entry.irq.IRQInfo2);
363	printk(" mem flags: 0x%x\n", parse.cftable_entry.mem.flags);
364	printk(" mem nwin: 0x%x\n", parse.cftable_entry.mem.nwin);
365	printk(" subtuples: 0x%x\n", parse.cftable_entry.subtuples);
366#endif
367
368#if 0
369	link->io.NumPorts1 = 0x20;
370	link->io.IOAddrLines = 5;
371	link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
372#endif
373	link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
374	link->io.IOAddrLines =
375		parse.cftable_entry.io.flags & CISTPL_IO_LINES_MASK;
376	link->io.NumPorts2 = 0;
377
378	{
379		int base;
380		for (base = 0x000; base < 0x400; base += 0x20) {
381			link->io.BasePort1 = base;
382			ret = pcmcia_request_io(link, &link->io);
383			//printk("RequestIO 0x%02x\n",ret);
384			if (!ret)
385				break;
386		}
387	}
388
389	link->irq.IRQInfo1 = parse.cftable_entry.irq.IRQInfo1;
390	link->irq.IRQInfo2 = parse.cftable_entry.irq.IRQInfo2;
391	ret = pcmcia_request_irq(link, &link->irq);
392	if (ret) {
393		printk("pcmcia_request_irq() returned error: %i\n", ret);
394	}
395	//printk("RequestIRQ 0x%02x\n",ret);
396
397	link->conf.ConfigIndex = 1;
398
399	ret = pcmcia_request_configuration(link, &link->conf);
400	//printk("RequestConfiguration %d\n",ret);
401
402	link->dev_node = &dev_node;
403}
404
405static int mio_cs_attach(comedi_device * dev, comedi_devconfig * it)
406{
407	struct pcmcia_device *link;
408	unsigned int irq;
409	int ret;
410
411	DPRINTK("mio_cs_attach(dev=%p,it=%p)\n", dev, it);
412
413	link = cur_dev;		/* XXX hack */
414	if (!link)
415		return -EIO;
416
417	dev->driver = &driver_ni_mio_cs;
418	dev->iobase = link->io.BasePort1;
419
420	irq = link->irq.AssignedIRQ;
421
422	printk("comedi%d: %s: DAQCard: io 0x%04lx, irq %u, ",
423		dev->minor, dev->driver->driver_name, dev->iobase, irq);
424
425#if 0
426	{
427		int i;
428
429		printk(" board fingerprint:");
430		for (i = 0; i < 32; i += 2) {
431			printk(" %04x %02x", inw(dev->iobase + i),
432				inb(dev->iobase + i + 1));
433		}
434		printk("\n");
435		printk(" board fingerprint (windowed):");
436		for (i = 0; i < 10; i++) {
437			printk(" 0x%04x", win_in(i));
438		}
439		printk("\n");
440	}
441#endif
442
443	dev->board_ptr = ni_boards + ni_getboardtype(dev, link);
444
445	printk(" %s", boardtype.name);
446	dev->board_name = boardtype.name;
447
448	if ((ret = comedi_request_irq(irq, ni_E_interrupt, NI_E_IRQ_FLAGS,
449				"ni_mio_cs", dev)) < 0) {
450		printk(" irq not available\n");
451		return -EINVAL;
452	}
453	dev->irq = irq;
454
455	/* allocate private area */
456	if ((ret = ni_alloc_private(dev)) < 0)
457		return ret;
458	devpriv->stc_writew = &mio_cs_win_out;
459	devpriv->stc_readw = &mio_cs_win_in;
460	devpriv->stc_writel = &win_out2;
461	devpriv->stc_readl = &win_in2;
462
463	if ((ret = ni_E_init(dev, it)) < 0) {
464		return ret;
465	}
466
467	return 0;
468}
469
470static int get_prodid(comedi_device * dev, struct pcmcia_device *link)
471{
472	tuple_t tuple;
473	u_short buf[128];
474	int prodid = 0;
475
476	tuple.TupleData = (cisdata_t *) buf;
477	tuple.TupleOffset = 0;
478	tuple.TupleDataMax = 255;
479	tuple.DesiredTuple = CISTPL_MANFID;
480	tuple.Attributes = TUPLE_RETURN_COMMON;
481	if ((pcmcia_get_first_tuple(link, &tuple) == 0) &&
482		(pcmcia_get_tuple_data(link, &tuple) == 0)) {
483		prodid = le16_to_cpu(buf[1]);
484	}
485
486	return prodid;
487}
488
489static int ni_getboardtype(comedi_device * dev, struct pcmcia_device *link)
490{
491	int id;
492	int i;
493
494	id = get_prodid(dev, link);
495
496	for (i = 0; i < n_ni_boards; i++) {
497		if (ni_boards[i].device_id == id) {
498			return i;
499		}
500	}
501
502	printk("unknown board 0x%04x -- pretend it is a ", id);
503
504	return 0;
505}
506
507#ifdef MODULE
508
509MODULE_LICENSE("GPL");
510
511static struct pcmcia_device_id ni_mio_cs_ids[] = {
512	PCMCIA_DEVICE_MANF_CARD(0x010b, 0x010d),	/* DAQCard-ai-16xe-50 */
513	PCMCIA_DEVICE_MANF_CARD(0x010b, 0x010c),	/* DAQCard-ai-16e-4 */
514	PCMCIA_DEVICE_MANF_CARD(0x010b, 0x02c4),	/* DAQCard-6062E */
515	PCMCIA_DEVICE_MANF_CARD(0x010b, 0x075e),	/* DAQCard-6024E */
516	PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0245),	/* DAQCard-6036E */
517	PCMCIA_DEVICE_NULL
518};
519
520MODULE_DEVICE_TABLE(pcmcia, ni_mio_cs_ids);
521
522struct pcmcia_driver ni_mio_cs_driver = {
523	.probe = &cs_attach,
524	.remove = &cs_detach,
525	.suspend = &mio_cs_suspend,
526	.resume = &mio_cs_resume,
527	.id_table = ni_mio_cs_ids,
528	.owner = THIS_MODULE,
529	.drv = {
530			.name = dev_info,
531		},
532};
533
534int init_module(void)
535{
536	pcmcia_register_driver(&ni_mio_cs_driver);
537	comedi_driver_register(&driver_ni_mio_cs);
538	return 0;
539}
540
541void cleanup_module(void)
542{
543	pcmcia_unregister_driver(&ni_mio_cs_driver);
544#if 0
545	while (cur_dev != NULL)
546		cs_detach(cur_dev->handle);
547#endif
548	comedi_driver_unregister(&driver_ni_mio_cs);
549}
550#endif
551