865f1f77cac5f808d0c5b5b6e1526e0f1ff265fd
[comedi.git] / comedi / drivers / ni_mio_cs.c
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 /*
24 Driver: ni_mio_cs.o
25 Description: National Instruments DAQCard E series
26 Author: ds
27 Status: works
28 Devices: [National Instruments] DAQCard-AI-16XE-50 (ni_mio_cs),
29   DAQCard-AI-16E-4, DAQCard-6062E, DAQCard-6024E
30
31 See the notes in the ni_atmio.o driver.
32 */
33 /*
34         The real guts of the driver is in ni_mio_common.c, which is
35         included by all the E series drivers.
36
37         References for specifications:
38         
39            341080a.pdf  DAQCard E Series Register Level Programmer Manual
40         
41 */
42
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>
48 #include <linux/mm.h>
49 #include <linux/interrupt.h>
50 #include <asm/io.h>
51 #include <linux/slab.h>
52 #ifdef CONFIG_COMEDI_RTL
53 #include <linux/rtl.h>
54 #endif
55 #include <linux/comedidev.h>
56 #include "ni_stc.h"
57 #include "8255.h"
58
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>
64
65 #undef DEBUG
66
67 #define ATMIO 1
68 #undef PCIMIO
69
70 /*
71  *  AT specific setup
72  */
73
74 #define NI_SIZE 0x20
75
76 #define MAX_N_CALDACS 12
77
78 static ni_board ni_boards[]={
79         {       device_id:      0x010d,
80                 name:           "DAQCard-ai-16xe-50",
81                 n_adchan:       16,
82                 adbits:         16,
83                 ai_fifo_depth:  1024,
84                 alwaysdither:   0,
85                 gainlkup:       ai_gain_8,
86                 ai_speed:       5000,
87                 n_aochan:       0,
88                 aobits:         0,
89                 ao_fifo_depth:  0,
90                 ao_unipolar:    0,
91                 has_8255:       0,
92                 caldac:         {dac8800,dac8043},
93         },
94         {       device_id:      0x010c,
95                 name:           "DAQCard-ai-16e-4",
96                 n_adchan:       16,
97                 adbits:         12,
98                 ai_fifo_depth:  1024,
99                 alwaysdither:   0,
100                 gainlkup:       ai_gain_16,
101                 ai_speed:       4000,
102                 n_aochan:       0,
103                 aobits:         0,
104                 ao_fifo_depth:  0,
105                 ao_unipolar:    0,
106                 has_8255:       0,
107                 caldac:         {mb88341}, /* or ad8804 */
108         },
109         {       device_id:      0x02c4,
110                 name:           "DAQCard-6062E",
111                 n_adchan:       16,
112                 adbits:         12,
113                 ai_fifo_depth:  1024,
114                 alwaysdither:   0,
115                 gainlkup:       ai_gain_16,
116                 ai_speed:       2000,
117                 n_aochan:       2,
118                 aobits:         12,
119                 ao_fifo_depth:  2048,
120                 ao_unipolar:    0,
121                 has_8255:       0,
122                 caldac:         {dac8800,dac8043},
123         },
124         {       device_id:      0x075e,
125                 name:           "DAQCard-6024E", /* specs incorrect! */
126                 n_adchan:       16,
127                 adbits:         12,
128                 ai_fifo_depth:  1024,
129                 alwaysdither:   0,
130                 gainlkup:       ai_gain_16,
131                 ai_speed:       5000,
132                 n_aochan:       2,
133                 aobits:         12,
134                 ao_fifo_depth:  0,
135                 ao_unipolar:    0,
136                 has_8255:       0,
137                 caldac:         {dac8800,dac8043},
138         },
139 #if 0
140         {       device_id:      0x0000, /* unknown */
141                 name:           "DAQCard-6715",
142                 n_adchan:       0,
143                 n_aochan:       8,
144                 aobits:         12,
145                 ao_671x:        8192,
146                 caldac:         {mb88341,mb88341},
147         },
148 #endif
149 };
150
151
152 #define interrupt_pin(a)        0
153
154 #define IRQ_POLARITY 1
155
156 #define NI_E_IRQ_FLAGS          SA_SHIRQ
157
158
159 /* How we access registers */
160
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))
167
168
169 typedef struct{
170         dev_link_t *link;
171
172         NI_PRIVATE_COMMON
173 }ni_private;
174 #define devpriv ((ni_private *)dev->private)
175
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",
180         module:         THIS_MODULE,
181         attach:         mio_cs_attach,
182         detach:         mio_cs_detach,
183 };
184
185
186 #include "ni_mio_common.c"
187
188
189 static int ni_getboardtype(comedi_device *dev,dev_link_t *link);
190
191 /* clean up allocated resources */
192 /* called when driver is removed */
193 static int mio_cs_detach(comedi_device *dev)
194 {
195         mio_common_detach(dev);
196
197         /* PCMCIA layer frees the IO region */
198
199         if(dev->irq){
200                 comedi_free_irq(dev->irq,dev);
201         }
202
203         return 0;
204 }
205
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 *);
209 static int irq_mask;
210
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 = {
214         "ni_mio_cs",
215         COMEDI_MAJOR,0,
216         NULL
217 };
218 static int mio_cs_event(event_t event, int priority, event_callback_args_t *args);
219
220 static void cs_error(client_handle_t handle, int func, int ret)
221 {
222         error_info_t err = { func, ret };
223
224         DPRINTK("cs_error(handle=%p, func=%d, ret=%d)\n",handle,func,ret);
225
226         CardServices(ReportError, handle, &err);
227 }
228
229 static dev_link_t *cs_attach(void)
230 {
231         dev_link_t *link;
232         client_reg_t client_reg;
233         int ret;
234
235         link=kmalloc(sizeof(*link),GFP_KERNEL);
236         if(!link)return NULL;
237         memset(link,0,sizeof(*link));
238
239         link->release.function = &cs_release;
240         link->release.data = (u_long)link;
241         
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;
248         link->conf.Vcc = 50;
249         link->conf.IntType = INT_MEMORY_AND_IO;
250         
251         link->next = dev_list;
252         dev_list = link;
253
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");
267                 cs_detach(link);
268                 return NULL;
269         }
270
271         return link;
272 }
273
274 static void cs_release(u_long arg)
275 {
276         dev_link_t *link=(void *)arg;
277
278         CardServices(ReleaseConfiguration, link->handle);
279         CardServices(ReleaseIO, link->handle, &link->io);
280         CardServices(ReleaseIRQ, link->handle, &link->irq);
281
282         link->state &= ~DEV_CONFIG;
283 }
284
285 static void cs_detach(dev_link_t *link)
286 {
287         dev_link_t **linkp;
288         
289         DPRINTK("cs_detach(link=%p)\n",link);
290         
291         for(linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
292                 if (*linkp == link) break;
293         if (*linkp==NULL)
294                 return;
295
296         //save_flags
297         //cli
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;
302         }
303         //restore_flags
304
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;
309                         return;
310                 }
311         }
312
313         if(link->handle){
314                 CardServices(DeregisterClient, link->handle);
315         }
316
317 }
318
319 static int mio_cs_event(event_t event, int priority, event_callback_args_t *args)
320 {
321         dev_link_t *link = args->client_data;
322
323         DPRINTK("mio_cs_event(event=%x,priority=%d,args=%p)\n",event,priority,args);
324
325         switch(event){
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);
333                 }
334                 /* XXX disable irq here, to get rid of spurious interrupts */
335                 break;
336         case CS_EVENT_CARD_INSERTION:
337                 DPRINTK("card insertion event\n");
338                 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
339                 mio_cs_config(link);
340                 break;
341         case CS_EVENT_PM_SUSPEND:
342                 DPRINTK("pm suspend event\n");
343                 link->state |= DEV_SUSPEND;
344                 /* fall through */
345         case CS_EVENT_RESET_PHYSICAL:
346                 DPRINTK("reset physical event\n");
347                 if(link->state & DEV_CONFIG)
348                         CardServices(ReleaseConfiguration, link->handle);
349                 break;
350         case CS_EVENT_PM_RESUME:
351                 DPRINTK("pm resume event\n");
352                 link->state &= ~DEV_SUSPEND;
353                 /* fall through */
354         case CS_EVENT_CARD_RESET:
355                 DPRINTK("card reset event\n");
356                 if(DEV_OK(link))
357                         CardServices(RequestConfiguration, link->handle, &link->conf);
358                 break;
359         default:
360                 DPRINTK("unknown event (ignored)\n");
361         }
362         return 0;
363 }
364
365
366
367 static void mio_cs_config(dev_link_t *link)
368 {
369         client_handle_t handle = link->handle;
370         tuple_t tuple;
371         u_short buf[128];
372         cisparse_t parse;
373         int manfid = 0, prodid = 0;
374         int ret;
375         config_info_t conf;
376
377         DPRINTK("mio_cs_config(link=%p)\n",link);
378
379         tuple.TupleData = (cisdata_t *)buf;
380         tuple.TupleOffset = 0;
381         tuple.TupleDataMax = 255;
382         tuple.Attributes = 0;
383         
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];
390
391         link->state |= DEV_CONFIG;
392
393         CardServices(GetConfigurationInfo,handle,&conf);
394         link->conf.Vcc=conf.Vcc;
395 #if 0
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);
399 #endif
400
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]);
407         }
408         //printk("manfid = 0x%04x, 0x%04x\n",manfid,prodid);
409
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);
415
416 #if 0
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);
428 #endif
429
430
431 #if 0
432         link->io.NumPorts1=0x20;
433         link->io.IOAddrLines=5;
434         link->io.Attributes1=IO_DATA_PATH_WIDTH_AUTO;
435 #endif
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;
439
440         {
441                 int base;
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);
446                         if(!ret)break;
447                 }
448         }
449
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);
454
455         link->conf.ConfigIndex=1;
456
457         ret=CardServices(RequestConfiguration, handle, &link->conf);
458         //printk("RequestConfiguration %d\n",ret);
459
460         link->dev = &dev_node;
461         link->state &= ~DEV_CONFIG_PENDING;
462 }
463
464 static int mio_cs_attach(comedi_device *dev,comedi_devconfig *it)
465 {
466         dev_link_t *link;
467         int ret;
468         
469         DPRINTK("mio_cs_attach(dev=%p,it=%p)\n",dev,it);
470
471         link = dev_list; /* XXX hack */
472         if(!link)return -EIO;
473
474         dev->driver=&driver_ni_mio_cs;
475         dev->iobase=link->io.BasePort1;
476
477         dev->irq=link->irq.AssignedIRQ;
478
479         printk("comedi%d: %s: DAQCard: io 0x%04x, irq %d, ",
480                 dev->minor,dev->driver->driver_name,dev->iobase,
481                 dev->irq);
482
483 #if 0
484         {
485                 int i;
486
487                 printk(" board fingerprint:");
488                 for(i=0;i<32;i+=2){
489                 printk(" %04x %02x",inw(dev->iobase+i),inb(dev->iobase+i+1));
490                 }
491                 printk("\n");
492                 printk(" board fingerprint (windowed):");
493                 for(i=0;i<10;i++){
494                         printk(" 0x%04x",win_in(i));
495                 }
496                 printk("\n");
497         }
498 #endif
499
500         dev->board_ptr = ni_boards + ni_getboardtype(dev,link);
501         
502         printk(" %s",boardtype.name);
503         dev->board_name=boardtype.name;
504
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");
507                 return -EINVAL;
508         }
509         
510         /* allocate private area */
511         if((ret=alloc_private(dev,sizeof(ni_private)))<0)
512                 return ret;
513         
514         if( (ret=ni_E_init(dev,it))<0 ){
515                 return ret;
516         }
517
518         return 0;
519 }
520
521
522 static int get_prodid(comedi_device *dev,dev_link_t *link)
523 {
524         client_handle_t handle = link->handle;
525         tuple_t tuple;
526         u_short buf[128];
527         int prodid = 0;
528
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]);
537         }
538         
539         return prodid;
540 }
541
542 static int ni_getboardtype(comedi_device *dev,dev_link_t *link)
543 {
544         int id;
545         int i;
546         
547         id = get_prodid(dev,link);
548
549         for(i=0;i<n_ni_boards;i++){
550                 if(ni_boards[i].device_id==id){
551                         return i;
552                 }
553         }
554
555         printk("unknown board 0x%04x -- pretend it is a ",id);
556
557         return 0;
558 }
559
560
561 #ifdef MODULE
562 int init_module(void)
563 {
564         servinfo_t serv;
565
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? */
571         }
572         register_pccard_driver(&dev_info, &cs_attach, &cs_detach);
573         comedi_driver_register(&driver_ni_mio_cs);
574         return 0;
575 }
576
577 void cleanup_module(void)
578 {
579         unregister_pccard_driver(&dev_info);
580 #if 0
581         while(dev_list != NULL)
582                 cs_detach(dev_list);
583 #endif
584         comedi_driver_unregister(&driver_ni_mio_cs);
585 }
586 #endif