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