2 comedi/drivers/ni_mio_cs.c
3 Hardware driver for NI PCMCIA MIO E series cards
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
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.
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.
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.
25 Description: National Instruments DAQCard E series
28 Devices: [National Instruments] DAQCard-AI-16XE-50 (ni_mio_cs),
29 DAQCard-AI-16E-4, DAQCard-6062E, DAQCard-6024E
31 See the notes in the ni_atmio.o driver.
34 The real guts of the driver is in ni_mio_common.c, which is
35 included by all the E series drivers.
37 References for specifications:
39 341080a.pdf DAQCard E Series Register Level Programmer Manual
43 #include <linux/kernel.h>
44 #include <linux/module.h>
45 #include <linux/errno.h>
46 #include <linux/ioport.h>
47 #include <linux/delay.h>
49 #include <linux/interrupt.h>
51 #include <linux/slab.h>
52 #ifdef CONFIG_COMEDI_RTL
53 #include <linux/rtl.h>
55 #include <linux/comedidev.h>
59 #include <pcmcia/version.h>
60 #include <pcmcia/cs_types.h>
61 #include <pcmcia/cs.h>
62 #include <pcmcia/cistpl.h>
63 #include <pcmcia/ds.h>
76 #define MAX_N_CALDACS 12
78 static ni_board ni_boards[]={
80 name: "DAQCard-ai-16xe-50",
92 caldac: {dac8800,dac8043},
95 name: "DAQCard-ai-16e-4",
100 gainlkup: ai_gain_16,
107 caldac: {mb88341}, /* or ad8804 */
110 name: "DAQCard-6062E",
115 gainlkup: ai_gain_16,
122 caldac: {dac8800,dac8043},
125 name: "DAQCard-6024E", /* specs incorrect! */
130 gainlkup: ai_gain_16,
137 caldac: {dac8800,dac8043},
140 { device_id: 0x0000, /* unknown */
141 name: "DAQCard-6715",
146 caldac: {mb88341,mb88341},
152 #define interrupt_pin(a) 0
154 #define IRQ_POLARITY 1
156 #define NI_E_IRQ_FLAGS SA_SHIRQ
159 /* How we access registers */
161 #define ni_writew(a,b) (outw((a),(b)+dev->iobase))
162 #define ni_readw(a) (inw((a)+dev->iobase))
163 #define ni_writeb(a,b) (outb((a),(b)+dev->iobase))
164 #define ni_readb(a) (inb((a)+dev->iobase))
165 #define ni_writeb_p(a,b) (outb_p((a),(b)+dev->iobase))
166 #define ni_readb_p(a) (inb_p((a)+dev->iobase))
174 #define devpriv ((ni_private *)dev->private)
176 static int mio_cs_attach(comedi_device *dev,comedi_devconfig *it);
177 static int mio_cs_detach(comedi_device *dev);
178 static comedi_driver driver_ni_mio_cs={
179 driver_name: "ni_mio_cs",
181 attach: mio_cs_attach,
182 detach: mio_cs_detach,
186 #include "ni_mio_common.c"
189 static int ni_getboardtype(comedi_device *dev,dev_link_t *link);
191 /* clean up allocated resources */
192 /* called when driver is removed */
193 static int mio_cs_detach(comedi_device *dev)
195 mio_common_detach(dev);
197 /* PCMCIA layer frees the IO region */
200 comedi_free_irq(dev->irq,dev);
206 static void mio_cs_config(dev_link_t *link);
207 static void cs_release(u_long arg);
208 static void cs_detach(dev_link_t *);
211 static dev_link_t *dev_list = NULL;
212 static dev_info_t dev_info = "ni_mio_cs";
213 static dev_node_t dev_node = {
218 static int mio_cs_event(event_t event, int priority, event_callback_args_t *args);
220 static void cs_error(client_handle_t handle, int func, int ret)
222 error_info_t err = { func, ret };
224 DPRINTK("cs_error(handle=%p, func=%d, ret=%d)\n",handle,func,ret);
226 CardServices(ReportError, handle, &err);
229 static dev_link_t *cs_attach(void)
232 client_reg_t client_reg;
235 link=kmalloc(sizeof(*link),GFP_KERNEL);
236 if(!link)return NULL;
237 memset(link,0,sizeof(*link));
239 link->release.function = &cs_release;
240 link->release.data = (u_long)link;
242 link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
243 link->io.NumPorts1 = 16;
244 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
245 link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
246 link->irq.IRQInfo2 = irq_mask;
247 link->conf.Attributes = CONF_ENABLE_IRQ;
249 link->conf.IntType = INT_MEMORY_AND_IO;
251 link->next = dev_list;
254 client_reg.dev_info = &dev_info;
255 client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
256 client_reg.EventMask =
257 CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
258 CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
259 CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
260 client_reg.event_handler = &mio_cs_event;
261 client_reg.Version = 0x0210;
262 client_reg.event_callback_args.client_data = link;
263 ret = CardServices(RegisterClient, &link->handle, &client_reg);
264 if (ret != CS_SUCCESS) {
265 cs_error(link->handle, RegisterClient, ret);
266 printk("detaching...\n");
274 static void cs_release(u_long arg)
276 dev_link_t *link=(void *)arg;
278 CardServices(ReleaseConfiguration, link->handle);
279 CardServices(ReleaseIO, link->handle, &link->io);
280 CardServices(ReleaseIRQ, link->handle, &link->irq);
282 link->state &= ~DEV_CONFIG;
285 static void cs_detach(dev_link_t *link)
289 DPRINTK("cs_detach(link=%p)\n",link);
291 for(linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
292 if (*linkp == link) break;
298 if (link->state & DEV_RELEASE_PENDING){
299 printk("dev release pending bug\n");
300 del_timer(&link->release);
301 link->state &= ~DEV_RELEASE_PENDING;
305 if(link->state & DEV_CONFIG) {
306 cs_release((u_long)link);
307 if(link->state & DEV_STALE_CONFIG) {
308 link->state |= DEV_STALE_LINK;
314 CardServices(DeregisterClient, link->handle);
319 static int mio_cs_event(event_t event, int priority, event_callback_args_t *args)
321 dev_link_t *link = args->client_data;
323 DPRINTK("mio_cs_event(event=%x,priority=%d,args=%p)\n",event,priority,args);
326 case CS_EVENT_CARD_REMOVAL:
327 DPRINTK("removal event\n");
328 link->state &= ~DEV_PRESENT;
329 if(link->state & DEV_CONFIG) {
330 link->release.expires = jiffies+HZ/20;
331 link->state |= DEV_RELEASE_PENDING;
332 add_timer(&link->release);
334 /* XXX disable irq here, to get rid of spurious interrupts */
336 case CS_EVENT_CARD_INSERTION:
337 DPRINTK("card insertion event\n");
338 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
341 case CS_EVENT_PM_SUSPEND:
342 DPRINTK("pm suspend event\n");
343 link->state |= DEV_SUSPEND;
345 case CS_EVENT_RESET_PHYSICAL:
346 DPRINTK("reset physical event\n");
347 if(link->state & DEV_CONFIG)
348 CardServices(ReleaseConfiguration, link->handle);
350 case CS_EVENT_PM_RESUME:
351 DPRINTK("pm resume event\n");
352 link->state &= ~DEV_SUSPEND;
354 case CS_EVENT_CARD_RESET:
355 DPRINTK("card reset event\n");
357 CardServices(RequestConfiguration, link->handle, &link->conf);
360 DPRINTK("unknown event (ignored)\n");
367 static void mio_cs_config(dev_link_t *link)
369 client_handle_t handle = link->handle;
373 int manfid = 0, prodid = 0;
377 DPRINTK("mio_cs_config(link=%p)\n",link);
379 tuple.TupleData = (cisdata_t *)buf;
380 tuple.TupleOffset = 0;
381 tuple.TupleDataMax = 255;
382 tuple.Attributes = 0;
384 tuple.DesiredTuple = CISTPL_CONFIG;
385 ret=CardServices(GetFirstTuple, handle, &tuple);
386 ret=CardServices(GetTupleData, handle, &tuple);
387 ret=CardServices(ParseTuple, handle, &tuple, &parse);
388 link->conf.ConfigBase = parse.config.base;
389 link->conf.Present = parse.config.rmask[0];
391 link->state |= DEV_CONFIG;
393 CardServices(GetConfigurationInfo,handle,&conf);
394 link->conf.Vcc=conf.Vcc;
396 tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
397 tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
398 info->multi (first_tuple(handle, &tuple, &parse) == CS_SUCCESS);
401 tuple.DesiredTuple = CISTPL_MANFID;
402 tuple.Attributes = TUPLE_RETURN_COMMON;
403 if((CardServices(GetFirstTuple,handle, &tuple) == CS_SUCCESS) &&
404 (CardServices(GetTupleData,handle,&tuple) == CS_SUCCESS)){
405 manfid = le16_to_cpu(buf[0]);
406 prodid = le16_to_cpu(buf[1]);
408 //printk("manfid = 0x%04x, 0x%04x\n",manfid,prodid);
410 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
411 tuple.Attributes = 0;
412 ret=CardServices(GetFirstTuple, handle, &tuple);
413 ret=CardServices(GetTupleData, handle, &tuple);
414 ret=CardServices(ParseTuple, handle, &tuple, &parse);
417 printk(" index: 0x%x\n",parse.cftable_entry.index);
418 printk(" flags: 0x%x\n",parse.cftable_entry.flags);
419 printk(" io flags: 0x%x\n",parse.cftable_entry.io.flags);
420 printk(" io nwin: 0x%x\n",parse.cftable_entry.io.nwin);
421 printk(" io base: 0x%x\n",parse.cftable_entry.io.win[0].base);
422 printk(" io len: 0x%x\n",parse.cftable_entry.io.win[0].len);
423 printk(" irq1: 0x%x\n",parse.cftable_entry.irq.IRQInfo1);
424 printk(" irq2: 0x%x\n",parse.cftable_entry.irq.IRQInfo2);
425 printk(" mem flags: 0x%x\n",parse.cftable_entry.mem.flags);
426 printk(" mem nwin: 0x%x\n",parse.cftable_entry.mem.nwin);
427 printk(" subtuples: 0x%x\n",parse.cftable_entry.subtuples);
432 link->io.NumPorts1=0x20;
433 link->io.IOAddrLines=5;
434 link->io.Attributes1=IO_DATA_PATH_WIDTH_AUTO;
436 link->io.NumPorts1=parse.cftable_entry.io.win[0].len;
437 link->io.IOAddrLines=parse.cftable_entry.io.flags & CISTPL_IO_LINES_MASK;
438 link->io.NumPorts2=0;
442 for(base=0x000;base<0x400;base+=0x20){
443 link->io.BasePort1=base;
444 ret=CardServices(RequestIO, handle, &link->io);
445 //printk("RequestIO 0x%02x\n",ret);
450 link->irq.IRQInfo1=parse.cftable_entry.irq.IRQInfo1;
451 link->irq.IRQInfo2=parse.cftable_entry.irq.IRQInfo2;
452 ret=CardServices(RequestIRQ, handle, &link->irq);
453 //printk("RequestIRQ 0x%02x\n",ret);
455 link->conf.ConfigIndex=1;
457 ret=CardServices(RequestConfiguration, handle, &link->conf);
458 //printk("RequestConfiguration %d\n",ret);
460 link->dev = &dev_node;
461 link->state &= ~DEV_CONFIG_PENDING;
464 static int mio_cs_attach(comedi_device *dev,comedi_devconfig *it)
469 DPRINTK("mio_cs_attach(dev=%p,it=%p)\n",dev,it);
471 link = dev_list; /* XXX hack */
472 if(!link)return -EIO;
474 dev->driver=&driver_ni_mio_cs;
475 dev->iobase=link->io.BasePort1;
477 dev->irq=link->irq.AssignedIRQ;
479 printk("comedi%d: %s: DAQCard: io 0x%04x, irq %d, ",
480 dev->minor,dev->driver->driver_name,dev->iobase,
487 printk(" board fingerprint:");
489 printk(" %04x %02x",inw(dev->iobase+i),inb(dev->iobase+i+1));
492 printk(" board fingerprint (windowed):");
494 printk(" 0x%04x",win_in(i));
500 dev->board_ptr = ni_boards + ni_getboardtype(dev,link);
502 printk(" %s",boardtype.name);
503 dev->board_name=boardtype.name;
505 if( (ret=comedi_request_irq(dev->irq,ni_E_interrupt,NI_E_IRQ_FLAGS,"ni_mio_cs",dev))<0 ){
506 printk(" irq not available\n");
510 /* allocate private area */
511 if((ret=alloc_private(dev,sizeof(ni_private)))<0)
514 if( (ret=ni_E_init(dev,it))<0 ){
522 static int get_prodid(comedi_device *dev,dev_link_t *link)
524 client_handle_t handle = link->handle;
529 tuple.TupleData = (cisdata_t *)buf;
530 tuple.TupleOffset = 0;
531 tuple.TupleDataMax = 255;
532 tuple.DesiredTuple = CISTPL_MANFID;
533 tuple.Attributes = TUPLE_RETURN_COMMON;
534 if((CardServices(GetFirstTuple,handle, &tuple) == CS_SUCCESS) &&
535 (CardServices(GetTupleData,handle,&tuple) == CS_SUCCESS)){
536 prodid = le16_to_cpu(buf[1]);
542 static int ni_getboardtype(comedi_device *dev,dev_link_t *link)
547 id = get_prodid(dev,link);
549 for(i=0;i<n_ni_boards;i++){
550 if(ni_boards[i].device_id==id){
555 printk("unknown board 0x%04x -- pretend it is a ",id);
562 int init_module(void)
566 CardServices(GetCardServicesInfo, &serv);
567 if(serv.Revision != CS_RELEASE_CODE){
568 printk(KERN_NOTICE "mio_cs: Card Services release "
569 "does not match!\n");
570 //return -EPERM; /* XXX what to return? */
572 register_pccard_driver(&dev_info, &cs_attach, &cs_detach);
573 comedi_driver_register(&driver_ni_mio_cs);
577 void cleanup_module(void)
579 unregister_pccard_driver(&dev_info);
581 while(dev_list != NULL)
584 comedi_driver_unregister(&driver_ni_mio_cs);