das08.c revision 0a85b6f0ab0d2edb0d41b32697111ce0e4f43496
1/* 2 comedi/drivers/das08.c 3 DAS08 driver 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 2000 David A. Schleef <ds@schleef.org> 7 Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net> 8 Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.org> 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 2 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 24***************************************************************** 25 26*/ 27/* 28Driver: das08 29Description: DAS-08 compatible boards 30Author: Warren Jasper, ds, Frank Hess 31Devices: [Keithley Metrabyte] DAS08 (isa-das08), [ComputerBoards] DAS08 (isa-das08), 32 DAS08-PGM (das08-pgm), 33 DAS08-PGH (das08-pgh), DAS08-PGL (das08-pgl), DAS08-AOH (das08-aoh), 34 DAS08-AOL (das08-aol), DAS08-AOM (das08-aom), DAS08/JR-AO (das08/jr-ao), 35 DAS08/JR-16-AO (das08jr-16-ao), PCI-DAS08 (das08), 36 PC104-DAS08 (pc104-das08), DAS08/JR/16 (das08jr/16) 37Status: works 38 39This is a rewrite of the das08 and das08jr drivers. 40 41Options (for ISA cards): 42 [0] - base io address 43 44Options (for pci-das08): 45 [0] - bus (optional) 46 [1] = slot (optional) 47 48The das08 driver doesn't support asynchronous commands, since 49the cheap das08 hardware doesn't really support them. The 50comedi_rt_timer driver can be used to emulate commands for this 51driver. 52*/ 53 54#include "../comedidev.h" 55 56#include <linux/delay.h> 57 58#include "comedi_pci.h" 59#include "8255.h" 60#include "das08.h" 61 62#define DRV_NAME "das08" 63 64#define PCI_VENDOR_ID_COMPUTERBOARDS 0x1307 65#define PCI_DEVICE_ID_PCIDAS08 0x29 66#define PCIDAS08_SIZE 0x54 67 68/* pci configuration registers */ 69#define INTCSR 0x4c 70#define INTR1_ENABLE 0x1 71#define INTR1_HIGH_POLARITY 0x2 72#define PCI_INTR_ENABLE 0x40 73#define INTR1_EDGE_TRIG 0x100 /* requires high polarity */ 74#define CNTRL 0x50 75#define CNTRL_DIR 0x2 76#define CNTRL_INTR 0x4 77 78/* 79 cio-das08.pdf 80 81 "isa-das08" 82 83 0 a/d bits 0-3 start 8 bit 84 1 a/d bits 4-11 start 12 bit 85 2 eoc, ip1-3, irq, mux op1-4, inte, mux 86 3 unused unused 87 4567 8254 88 89ab 8255 89 90 requires hard-wiring for async ai 91 92*/ 93 94#define DAS08_LSB 0 95#define DAS08_MSB 1 96#define DAS08_TRIG_12BIT 1 97#define DAS08_STATUS 2 98#define DAS08_EOC (1<<7) 99#define DAS08_IRQ (1<<3) 100#define DAS08_IP(x) (((x)>>4)&0x7) 101#define DAS08_CONTROL 2 102#define DAS08_MUX_MASK 0x7 103#define DAS08_MUX(x) ((x) & DAS08_MUX_MASK) 104#define DAS08_INTE (1<<3) 105#define DAS08_DO_MASK 0xf0 106#define DAS08_OP(x) (((x) << 4) & DAS08_DO_MASK) 107 108/* 109 cio-das08jr.pdf 110 111 "das08/jr-ao" 112 113 0 a/d bits 0-3 unused 114 1 a/d bits 4-11 start 12 bit 115 2 eoc, mux mux 116 3 di do 117 4 unused ao0_lsb 118 5 unused ao0_msb 119 6 unused ao1_lsb 120 7 unused ao1_msb 121 122*/ 123 124#define DAS08JR_DIO 3 125#define DAS08JR_AO_LSB(x) ((x)?6:4) 126#define DAS08JR_AO_MSB(x) ((x)?7:5) 127 128/* 129 cio-das08_aox.pdf 130 131 "das08-aoh" 132 "das08-aol" 133 "das08-aom" 134 135 0 a/d bits 0-3 start 8 bit 136 1 a/d bits 4-11 start 12 bit 137 2 eoc, ip1-3, irq, mux op1-4, inte, mux 138 3 mux, gain status gain control 139 4567 8254 140 8 unused ao0_lsb 141 9 unused ao0_msb 142 a unused ao1_lsb 143 b unused ao1_msb 144 89ab 145 cdef 8255 146*/ 147 148#define DAS08AO_GAIN_CONTROL 3 149#define DAS08AO_GAIN_STATUS 3 150 151#define DAS08AO_AO_LSB(x) ((x)?0xa:8) 152#define DAS08AO_AO_MSB(x) ((x)?0xb:9) 153#define DAS08AO_AO_UPDATE 8 154 155/* gainlist same as _pgx_ below */ 156 157static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 158 struct comedi_insn *insn, unsigned int *data); 159static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s, 160 struct comedi_insn *insn, unsigned int *data); 161static int das08_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s, 162 struct comedi_insn *insn, unsigned int *data); 163static int das08jr_di_rbits(struct comedi_device *dev, 164 struct comedi_subdevice *s, 165 struct comedi_insn *insn, unsigned int *data); 166static int das08jr_do_wbits(struct comedi_device *dev, 167 struct comedi_subdevice *s, 168 struct comedi_insn *insn, unsigned int *data); 169static int das08jr_ao_winsn(struct comedi_device *dev, 170 struct comedi_subdevice *s, 171 struct comedi_insn *insn, unsigned int *data); 172static int das08ao_ao_winsn(struct comedi_device *dev, 173 struct comedi_subdevice *s, 174 struct comedi_insn *insn, unsigned int *data); 175static void i8254_set_mode_low(unsigned int base, int channel, 176 unsigned int mode); 177 178static const struct comedi_lrange range_das08_pgl = { 9, { 179 BIP_RANGE(10), 180 BIP_RANGE(5), 181 BIP_RANGE(2.5), 182 BIP_RANGE(1.25), 183 BIP_RANGE(0.625), 184 UNI_RANGE(10), 185 UNI_RANGE(5), 186 UNI_RANGE(2.5), 187 UNI_RANGE(1.25) 188 } 189}; 190 191static const struct comedi_lrange range_das08_pgh = { 12, { 192 BIP_RANGE(10), 193 BIP_RANGE(5), 194 BIP_RANGE(1), 195 BIP_RANGE(0.5), 196 BIP_RANGE(0.1), 197 BIP_RANGE(0.05), 198 BIP_RANGE(0.01), 199 BIP_RANGE(0.005), 200 UNI_RANGE(10), 201 UNI_RANGE(1), 202 UNI_RANGE(0.1), 203 UNI_RANGE(0.01), 204 } 205}; 206 207static const struct comedi_lrange range_das08_pgm = { 9, { 208 BIP_RANGE(10), 209 BIP_RANGE(5), 210 BIP_RANGE(0.5), 211 BIP_RANGE(0.05), 212 BIP_RANGE(0.01), 213 UNI_RANGE(10), 214 UNI_RANGE(1), 215 UNI_RANGE(0.1), 216 UNI_RANGE(0.01) 217 } 218}; /* 219 cio-das08jr.pdf 220 221 "das08/jr-ao" 222 223 0 a/d bits 0-3 unused 224 1 a/d bits 4-11 start 12 bit 225 2 eoc, mux mux 226 3 di do 227 4 unused ao0_lsb 228 5 unused ao0_msb 229 6 unused ao1_lsb 230 7 unused ao1_msb 231 232 */ 233 234static const struct comedi_lrange *const das08_ai_lranges[] = { 235 &range_unknown, 236 &range_bipolar5, 237 &range_das08_pgh, 238 &range_das08_pgl, 239 &range_das08_pgm, 240}; 241 242static const int das08_pgh_gainlist[] = 243 { 8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7 }; 244static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 }; 245static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 }; 246 247static const int *const das08_gainlists[] = { 248 NULL, 249 NULL, 250 das08_pgh_gainlist, 251 das08_pgl_gainlist, 252 das08_pgm_gainlist, 253}; 254 255static const struct das08_board_struct das08_boards[] = { 256 { 257 .name = "isa-das08", /* cio-das08.pdf */ 258 .bustype = isa, 259 .ai = das08_ai_rinsn, 260 .ai_nbits = 12, 261 .ai_pg = das08_pg_none, 262 .ai_encoding = das08_encode12, 263 .ao = NULL, 264 .ao_nbits = 12, 265 .di = das08_di_rbits, 266 .do_ = das08_do_wbits, 267 .do_nchan = 4, 268 .i8255_offset = 8, 269 .i8254_offset = 4, 270 .iosize = 16, /* unchecked */ 271 }, 272 { 273 .name = "das08-pgm", /* cio-das08pgx.pdf */ 274 .bustype = isa, 275 .ai = das08_ai_rinsn, 276 .ai_nbits = 12, 277 .ai_pg = das08_pgm, 278 .ai_encoding = das08_encode12, 279 .ao = NULL, 280 .di = das08_di_rbits, 281 .do_ = das08_do_wbits, 282 .do_nchan = 4, 283 .i8255_offset = 0, 284 .i8254_offset = 0x04, 285 .iosize = 16, /* unchecked */ 286 }, 287 { 288 .name = "das08-pgh", /* cio-das08pgx.pdf */ 289 .bustype = isa, 290 .ai = das08_ai_rinsn, 291 .ai_nbits = 12, 292 .ai_pg = das08_pgh, 293 .ai_encoding = das08_encode12, 294 .ao = NULL, 295 .di = das08_di_rbits, 296 .do_ = das08_do_wbits, 297 .do_nchan = 4, 298 .i8255_offset = 0, 299 .i8254_offset = 0x04, 300 .iosize = 16, /* unchecked */ 301 }, 302 { 303 .name = "das08-pgl", /* cio-das08pgx.pdf */ 304 .bustype = isa, 305 .ai = das08_ai_rinsn, 306 .ai_nbits = 12, 307 .ai_pg = das08_pgl, 308 .ai_encoding = das08_encode12, 309 .ao = NULL, 310 .di = das08_di_rbits, 311 .do_ = das08_do_wbits, 312 .do_nchan = 4, 313 .i8255_offset = 0, 314 .i8254_offset = 0x04, 315 .iosize = 16, /* unchecked */ 316 }, 317 { 318 .name = "das08-aoh", /* cio-das08_aox.pdf */ 319 .bustype = isa, 320 .ai = das08_ai_rinsn, 321 .ai_nbits = 12, 322 .ai_pg = das08_pgh, 323 .ai_encoding = das08_encode12, 324 .ao = das08ao_ao_winsn, /* 8 */ 325 .ao_nbits = 12, 326 .di = das08_di_rbits, 327 .do_ = das08_do_wbits, 328 .do_nchan = 4, 329 .i8255_offset = 0x0c, 330 .i8254_offset = 0x04, 331 .iosize = 16, /* unchecked */ 332 }, 333 { 334 .name = "das08-aol", /* cio-das08_aox.pdf */ 335 .bustype = isa, 336 .ai = das08_ai_rinsn, 337 .ai_nbits = 12, 338 .ai_pg = das08_pgl, 339 .ai_encoding = das08_encode12, 340 .ao = das08ao_ao_winsn, /* 8 */ 341 .ao_nbits = 12, 342 .di = das08_di_rbits, 343 .do_ = das08_do_wbits, 344 .do_nchan = 4, 345 .i8255_offset = 0x0c, 346 .i8254_offset = 0x04, 347 .iosize = 16, /* unchecked */ 348 }, 349 { 350 .name = "das08-aom", /* cio-das08_aox.pdf */ 351 .bustype = isa, 352 .ai = das08_ai_rinsn, 353 .ai_nbits = 12, 354 .ai_pg = das08_pgm, 355 .ai_encoding = das08_encode12, 356 .ao = das08ao_ao_winsn, /* 8 */ 357 .ao_nbits = 12, 358 .di = das08_di_rbits, 359 .do_ = das08_do_wbits, 360 .do_nchan = 4, 361 .i8255_offset = 0x0c, 362 .i8254_offset = 0x04, 363 .iosize = 16, /* unchecked */ 364 }, 365 { 366 .name = "das08/jr-ao", /* cio-das08-jr-ao.pdf */ 367 .bustype = isa, 368 .ai = das08_ai_rinsn, 369 .ai_nbits = 12, 370 .ai_pg = das08_pg_none, 371 .ai_encoding = das08_encode12, 372 .ao = das08jr_ao_winsn, 373 .ao_nbits = 12, 374 .di = das08jr_di_rbits, 375 .do_ = das08jr_do_wbits, 376 .do_nchan = 8, 377 .i8255_offset = 0, 378 .i8254_offset = 0, 379 .iosize = 16, /* unchecked */ 380 }, 381 { 382 .name = "das08jr-16-ao", /* cio-das08jr-16-ao.pdf */ 383 .bustype = isa, 384 .ai = das08_ai_rinsn, 385 .ai_nbits = 16, 386 .ai_pg = das08_pg_none, 387 .ai_encoding = das08_encode12, 388 .ao = das08jr_ao_winsn, 389 .ao_nbits = 16, 390 .di = das08jr_di_rbits, 391 .do_ = das08jr_do_wbits, 392 .do_nchan = 8, 393 .i8255_offset = 0, 394 .i8254_offset = 0x04, 395 .iosize = 16, /* unchecked */ 396 }, 397#ifdef CONFIG_COMEDI_PCI 398 { 399 .name = "das08", /* pci-das08 */ 400 .id = PCI_DEVICE_ID_PCIDAS08, 401 .bustype = pci, 402 .ai = das08_ai_rinsn, 403 .ai_nbits = 12, 404 .ai_pg = das08_bipolar5, 405 .ai_encoding = das08_encode12, 406 .ao = NULL, 407 .ao_nbits = 0, 408 .di = das08_di_rbits, 409 .do_ = das08_do_wbits, 410 .do_nchan = 4, 411 .i8255_offset = 0, 412 .i8254_offset = 4, 413 .iosize = 8, 414 }, 415#endif 416 { 417 .name = "pc104-das08", 418 .bustype = pc104, 419 .ai = das08_ai_rinsn, 420 .ai_nbits = 12, 421 .ai_pg = das08_pg_none, 422 .ai_encoding = das08_encode12, 423 .ao = NULL, 424 .ao_nbits = 0, 425 .di = das08_di_rbits, 426 .do_ = das08_do_wbits, 427 .do_nchan = 4, 428 .i8255_offset = 0, 429 .i8254_offset = 4, 430 .iosize = 16, /* unchecked */ 431 }, 432#if 0 433 { 434 .name = "das08/f", 435 }, 436 { 437 .name = "das08jr", 438 }, 439#endif 440 { 441 .name = "das08jr/16", 442 .bustype = isa, 443 .ai = das08_ai_rinsn, 444 .ai_nbits = 16, 445 .ai_pg = das08_pg_none, 446 .ai_encoding = das08_encode16, 447 .ao = NULL, 448 .ao_nbits = 0, 449 .di = das08jr_di_rbits, 450 .do_ = das08jr_do_wbits, 451 .do_nchan = 8, 452 .i8255_offset = 0, 453 .i8254_offset = 0, 454 .iosize = 16, /* unchecked */ 455 }, 456#if 0 457 { 458 .name = "das48-pga", /* cio-das48-pga.pdf */ 459 }, 460 { 461 .name = "das08-pga-g2", /* a KM board */ 462 }, 463#endif 464}; 465 466#ifdef CONFIG_COMEDI_PCMCIA 467struct das08_board_struct das08_cs_boards[NUM_DAS08_CS_BOARDS] = { 468 { 469 .name = "pcm-das08", 470 .id = 0x0, /* XXX */ 471 .bustype = pcmcia, 472 .ai = das08_ai_rinsn, 473 .ai_nbits = 12, 474 .ai_pg = das08_bipolar5, 475 .ai_encoding = das08_pcm_encode12, 476 .ao = NULL, 477 .ao_nbits = 0, 478 .di = das08_di_rbits, 479 .do_ = das08_do_wbits, 480 .do_nchan = 3, 481 .i8255_offset = 0, 482 .i8254_offset = 0, 483 .iosize = 16, 484 }, 485 /* duplicate so driver name can be used also */ 486 { 487 .name = "das08_cs", 488 .id = 0x0, /* XXX */ 489 .bustype = pcmcia, 490 .ai = das08_ai_rinsn, 491 .ai_nbits = 12, 492 .ai_pg = das08_bipolar5, 493 .ai_encoding = das08_pcm_encode12, 494 .ao = NULL, 495 .ao_nbits = 0, 496 .di = das08_di_rbits, 497 .do_ = das08_do_wbits, 498 .do_nchan = 3, 499 .i8255_offset = 0, 500 .i8254_offset = 0, 501 .iosize = 16, 502 }, 503}; 504#endif 505 506#ifdef CONFIG_COMEDI_PCI 507static DEFINE_PCI_DEVICE_TABLE(das08_pci_table) = { 508 { 509 PCI_VENDOR_ID_COMPUTERBOARDS, PCI_DEVICE_ID_PCIDAS08, 510 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { 511 0} 512}; 513 514MODULE_DEVICE_TABLE(pci, das08_pci_table); 515#endif 516 517#define devpriv ((struct das08_private_struct *)dev->private) 518#define thisboard ((const struct das08_board_struct *)dev->board_ptr) 519 520#define TIMEOUT 100000 521 522static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 523 struct comedi_insn *insn, unsigned int *data) 524{ 525 int i, n; 526 int chan; 527 int range; 528 int lsb, msb; 529 530 chan = CR_CHAN(insn->chanspec); 531 range = CR_RANGE(insn->chanspec); 532 533 /* clear crap */ 534 inb(dev->iobase + DAS08_LSB); 535 inb(dev->iobase + DAS08_MSB); 536 537 /* set multiplexer */ 538 spin_lock(&dev->spinlock); /* lock to prevent race with digital output */ 539 devpriv->do_mux_bits &= ~DAS08_MUX_MASK; 540 devpriv->do_mux_bits |= DAS08_MUX(chan); 541 outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL); 542 spin_unlock(&dev->spinlock); 543 544 if (s->range_table->length > 1) { 545 /* set gain/range */ 546 range = CR_RANGE(insn->chanspec); 547 outb(devpriv->pg_gainlist[range], 548 dev->iobase + DAS08AO_GAIN_CONTROL); 549 } 550 551 for (n = 0; n < insn->n; n++) { 552 /* clear over-range bits for 16-bit boards */ 553 if (thisboard->ai_nbits == 16) 554 if (inb(dev->iobase + DAS08_MSB) & 0x80) 555 printk("das08: over-range\n"); 556 557 /* trigger conversion */ 558 outb_p(0, dev->iobase + DAS08_TRIG_12BIT); 559 560 for (i = 0; i < TIMEOUT; i++) { 561 if (!(inb(dev->iobase + DAS08_STATUS) & DAS08_EOC)) 562 break; 563 } 564 if (i == TIMEOUT) { 565 printk("das08: timeout\n"); 566 return -ETIME; 567 } 568 msb = inb(dev->iobase + DAS08_MSB); 569 lsb = inb(dev->iobase + DAS08_LSB); 570 if (thisboard->ai_encoding == das08_encode12) { 571 data[n] = (lsb >> 4) | (msb << 4); 572 } else if (thisboard->ai_encoding == das08_pcm_encode12) { 573 data[n] = (msb << 8) + lsb; 574 } else if (thisboard->ai_encoding == das08_encode16) { 575 /* FPOS 16-bit boards are sign-magnitude */ 576 if (msb & 0x80) 577 data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8); 578 else 579 data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8); 580 } else { 581 comedi_error(dev, "bug! unknown ai encoding"); 582 return -1; 583 } 584 } 585 586 return n; 587} 588 589static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s, 590 struct comedi_insn *insn, unsigned int *data) 591{ 592 data[0] = 0; 593 data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS)); 594 595 return 2; 596} 597 598static int das08_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s, 599 struct comedi_insn *insn, unsigned int *data) 600{ 601 int wbits; 602 603 /* get current settings of digital output lines */ 604 wbits = (devpriv->do_mux_bits >> 4) & 0xf; 605 /* null bits we are going to set */ 606 wbits &= ~data[0]; 607 /* set new bit values */ 608 wbits |= data[0] & data[1]; 609 /* remember digital output bits */ 610 spin_lock(&dev->spinlock); /* prevent race with setting of analog input mux */ 611 devpriv->do_mux_bits &= ~DAS08_DO_MASK; 612 devpriv->do_mux_bits |= DAS08_OP(wbits); 613 outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL); 614 spin_unlock(&dev->spinlock); 615 616 data[1] = wbits; 617 618 return 2; 619} 620 621static int das08jr_di_rbits(struct comedi_device *dev, 622 struct comedi_subdevice *s, 623 struct comedi_insn *insn, unsigned int *data) 624{ 625 data[0] = 0; 626 data[1] = inb(dev->iobase + DAS08JR_DIO); 627 628 return 2; 629} 630 631static int das08jr_do_wbits(struct comedi_device *dev, 632 struct comedi_subdevice *s, 633 struct comedi_insn *insn, unsigned int *data) 634{ 635 /* null bits we are going to set */ 636 devpriv->do_bits &= ~data[0]; 637 /* set new bit values */ 638 devpriv->do_bits |= data[0] & data[1]; 639 outb(devpriv->do_bits, dev->iobase + DAS08JR_DIO); 640 641 data[1] = devpriv->do_bits; 642 643 return 2; 644} 645 646static int das08jr_ao_winsn(struct comedi_device *dev, 647 struct comedi_subdevice *s, 648 struct comedi_insn *insn, unsigned int *data) 649{ 650 int n; 651 int lsb, msb; 652 int chan; 653 654 lsb = data[0] & 0xff; 655 msb = (data[0] >> 8) & 0xf; 656 657 chan = CR_CHAN(insn->chanspec); 658 659 for (n = 0; n < insn->n; n++) { 660#if 0 661 outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]); 662 outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]); 663#else 664 outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan)); 665 outb(msb, dev->iobase + DAS08JR_AO_MSB(chan)); 666#endif 667 668 /* load DACs */ 669 inb(dev->iobase + DAS08JR_DIO); 670 } 671 672 return n; 673} 674 675/* 676 * 677 * The -aox boards have the DACs at a different offset and use 678 * a different method to force an update. 679 * 680 */ 681static int das08ao_ao_winsn(struct comedi_device *dev, 682 struct comedi_subdevice *s, 683 struct comedi_insn *insn, unsigned int *data) 684{ 685 int n; 686 int lsb, msb; 687 int chan; 688 689 lsb = data[0] & 0xff; 690 msb = (data[0] >> 8) & 0xf; 691 692 chan = CR_CHAN(insn->chanspec); 693 694 for (n = 0; n < insn->n; n++) { 695#if 0 696 outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]); 697 outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]); 698#else 699 outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan)); 700 outb(msb, dev->iobase + DAS08AO_AO_MSB(chan)); 701#endif 702 703 /* load DACs */ 704 inb(dev->iobase + DAS08AO_AO_UPDATE); 705 } 706 707 return n; 708} 709 710static unsigned int i8254_read_channel_low(unsigned int base, int chan) 711{ 712 unsigned int msb, lsb; 713 714 /* The following instructions must be in order. 715 We must avoid other process reading the counter's value in the 716 middle. 717 The spin_lock isn't needed since ioctl calls grab the big kernel 718 lock automatically */ 719 /*spin_lock(sp); */ 720 outb(chan << 6, base + I8254_CTRL); 721 base += chan; 722 lsb = inb(base); 723 msb = inb(base); 724 /*spin_unlock(sp); */ 725 726 return lsb | (msb << 8); 727} 728 729static void i8254_write_channel_low(unsigned int base, int chan, 730 unsigned int value) 731{ 732 unsigned int msb, lsb; 733 734 lsb = value & 0xFF; 735 msb = value >> 8; 736 737 /* write lsb, then msb */ 738 base += chan; 739 /* See comments in i8254_read_channel_low */ 740 /*spin_lock(sp); */ 741 outb(lsb, base); 742 outb(msb, base); 743 /*spin_unlock(sp); */ 744} 745 746static unsigned int i8254_read_channel(struct i8254_struct *st, int channel) 747{ 748 int chan = st->logic2phys[channel]; 749 750 return i8254_read_channel_low(st->iobase, chan); 751} 752 753static void i8254_write_channel(struct i8254_struct *st, int channel, 754 unsigned int value) 755{ 756 int chan = st->logic2phys[channel]; 757 758 i8254_write_channel_low(st->iobase, chan, value); 759} 760 761static void i8254_initialize(struct i8254_struct *st) 762{ 763 int i; 764 for (i = 0; i < 3; ++i) 765 i8254_set_mode_low(st->iobase, i, st->mode[i]); 766} 767 768static void i8254_set_mode_low(unsigned int base, int channel, 769 unsigned int mode) 770{ 771 outb((channel << 6) | 0x30 | (mode & 0x0F), base + I8254_CTRL); 772} 773 774static void i8254_set_mode(struct i8254_struct *st, int channel, 775 unsigned int mode) 776{ 777 int chan = st->logic2phys[channel]; 778 779 st->mode[chan] = mode; 780 return i8254_set_mode_low(st->iobase, chan, mode); 781} 782 783static unsigned int i8254_read_status_low(unsigned int base, int channel) 784{ 785 outb(0xE0 | (2 << channel), base + I8254_CTRL); 786 return inb(base + channel); 787} 788 789static unsigned int i8254_read_status(struct i8254_struct *st, int channel) 790{ 791 int chan = st->logic2phys[channel]; 792 793 return i8254_read_status_low(st->iobase, chan); 794} 795 796static int das08_counter_read(struct comedi_device *dev, 797 struct comedi_subdevice *s, 798 struct comedi_insn *insn, unsigned int *data) 799{ 800 int chan = insn->chanspec; 801 802 /* printk("Reading counter channel %d ",chan); */ 803 data[0] = i8254_read_channel(&devpriv->i8254, chan); 804 /* printk("=> 0x%08X\n",data[0]); */ 805 806 return 1; 807} 808 809static int das08_counter_write(struct comedi_device *dev, 810 struct comedi_subdevice *s, 811 struct comedi_insn *insn, unsigned int *data) 812{ 813 int chan = insn->chanspec; 814 815 /* printk("Writing counter channel %d with 0x%04X\n",chan,data[0]); */ 816 i8254_write_channel(&devpriv->i8254, chan, data[0]); 817 818 return 1; 819} 820 821static int das08_counter_config(struct comedi_device *dev, 822 struct comedi_subdevice *s, 823 struct comedi_insn *insn, unsigned int *data) 824{ 825 int chan = insn->chanspec; 826 827 if (insn->n != 2) 828 return -EINVAL; 829 830 switch (data[0]) { 831 case INSN_CONFIG_SET_COUNTER_MODE: 832 i8254_set_mode(&devpriv->i8254, chan, data[1]); 833 break; 834 case INSN_CONFIG_8254_READ_STATUS: 835 data[1] = i8254_read_status(&devpriv->i8254, chan); 836 break; 837 default: 838 return -EINVAL; 839 break; 840 } 841 return 2; 842} 843 844static int das08_attach(struct comedi_device *dev, struct comedi_devconfig *it); 845 846static struct comedi_driver driver_das08 = { 847 .driver_name = DRV_NAME, 848 .module = THIS_MODULE, 849 .attach = das08_attach, 850 .detach = das08_common_detach, 851 .board_name = &das08_boards[0].name, 852 .num_names = sizeof(das08_boards) / sizeof(struct das08_board_struct), 853 .offset = sizeof(struct das08_board_struct), 854}; 855 856int das08_common_attach(struct comedi_device *dev, unsigned long iobase) 857{ 858 struct comedi_subdevice *s; 859 int ret; 860 861 /* allocate ioports for non-pcmcia, non-pci boards */ 862 if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) { 863 printk(" iobase 0x%lx\n", iobase); 864 if (!request_region(iobase, thisboard->iosize, DRV_NAME)) { 865 printk(" I/O port conflict\n"); 866 return -EIO; 867 } 868 } 869 dev->iobase = iobase; 870 871 dev->board_name = thisboard->name; 872 873 ret = alloc_subdevices(dev, 6); 874 if (ret < 0) 875 return ret; 876 877 s = dev->subdevices + 0; 878 /* ai */ 879 if (thisboard->ai) { 880 s->type = COMEDI_SUBD_AI; 881 /* XXX some boards actually have differential inputs instead of single ended. 882 * The driver does nothing with arefs though, so it's no big deal. */ 883 s->subdev_flags = SDF_READABLE | SDF_GROUND; 884 s->n_chan = 8; 885 s->maxdata = (1 << thisboard->ai_nbits) - 1; 886 s->range_table = das08_ai_lranges[thisboard->ai_pg]; 887 s->insn_read = thisboard->ai; 888 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg]; 889 } else { 890 s->type = COMEDI_SUBD_UNUSED; 891 } 892 893 s = dev->subdevices + 1; 894 /* ao */ 895 if (thisboard->ao) { 896 s->type = COMEDI_SUBD_AO; 897/* XXX lacks read-back insn */ 898 s->subdev_flags = SDF_WRITABLE; 899 s->n_chan = 2; 900 s->maxdata = (1 << thisboard->ao_nbits) - 1; 901 s->range_table = &range_bipolar5; 902 s->insn_write = thisboard->ao; 903 } else { 904 s->type = COMEDI_SUBD_UNUSED; 905 } 906 907 s = dev->subdevices + 2; 908 /* di */ 909 if (thisboard->di) { 910 s->type = COMEDI_SUBD_DI; 911 s->subdev_flags = SDF_READABLE; 912 s->n_chan = (thisboard->di == das08_di_rbits) ? 3 : 8; 913 s->maxdata = 1; 914 s->range_table = &range_digital; 915 s->insn_bits = thisboard->di; 916 } else { 917 s->type = COMEDI_SUBD_UNUSED; 918 } 919 920 s = dev->subdevices + 3; 921 /* do */ 922 if (thisboard->do_) { 923 s->type = COMEDI_SUBD_DO; 924 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 925 s->n_chan = thisboard->do_nchan; 926 s->maxdata = 1; 927 s->range_table = &range_digital; 928 s->insn_bits = thisboard->do_; 929 } else { 930 s->type = COMEDI_SUBD_UNUSED; 931 } 932 933 s = dev->subdevices + 4; 934 /* 8255 */ 935 if (thisboard->i8255_offset != 0) { 936 subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase + 937 thisboard-> 938 i8255_offset)); 939 } else { 940 s->type = COMEDI_SUBD_UNUSED; 941 } 942 943 s = dev->subdevices + 5; 944 /* 8254 */ 945 if (thisboard->i8254_offset != 0) { 946 s->type = COMEDI_SUBD_COUNTER; 947 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 948 s->n_chan = 3; 949 s->maxdata = 0xFFFF; 950 s->insn_read = das08_counter_read; 951 s->insn_write = das08_counter_write; 952 s->insn_config = das08_counter_config; 953 /* Set-up the 8254 structure */ 954 devpriv->i8254.channels = 3; 955 devpriv->i8254.logic2phys[0] = 0; 956 devpriv->i8254.logic2phys[1] = 1; 957 devpriv->i8254.logic2phys[2] = 2; 958 devpriv->i8254.iobase = iobase + thisboard->i8254_offset; 959 devpriv->i8254.mode[0] = 960 devpriv->i8254.mode[1] = 961 devpriv->i8254.mode[2] = I8254_MODE0 | I8254_BINARY; 962 i8254_initialize(&devpriv->i8254); 963 } else { 964 s->type = COMEDI_SUBD_UNUSED; 965 } 966 967 return 0; 968} 969 970static int das08_attach(struct comedi_device *dev, struct comedi_devconfig *it) 971{ 972 int ret; 973 unsigned long iobase; 974#ifdef CONFIG_COMEDI_PCI 975 unsigned long pci_iobase = 0; 976 struct pci_dev *pdev; 977#endif 978 979 ret = alloc_private(dev, sizeof(struct das08_private_struct)); 980 if (ret < 0) 981 return ret; 982 983 printk("comedi%d: das08: ", dev->minor); 984 /* deal with a pci board */ 985 if (thisboard->bustype == pci) { 986#ifdef CONFIG_COMEDI_PCI 987 if (it->options[0] || it->options[1]) { 988 printk("bus %i slot %i ", 989 it->options[0], it->options[1]); 990 } 991 printk("\n"); 992 /* find card */ 993 for (pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); 994 pdev != NULL; 995 pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) { 996 if (pdev->vendor == PCI_VENDOR_ID_COMPUTERBOARDS 997 && pdev->device == PCI_DEVICE_ID_PCIDAS08) { 998 if (it->options[0] || it->options[1]) { 999 if (pdev->bus->number == it->options[0] 1000 && PCI_SLOT(pdev->devfn) == 1001 it->options[1]) { 1002 break; 1003 } 1004 } else { 1005 break; 1006 } 1007 } 1008 } 1009 if (!pdev) { 1010 printk("No pci das08 cards found\n"); 1011 return -EIO; 1012 } 1013 devpriv->pdev = pdev; 1014 /* enable PCI device and reserve I/O spaces */ 1015 if (comedi_pci_enable(pdev, DRV_NAME)) { 1016 printk 1017 (" Error enabling PCI device and requesting regions\n"); 1018 return -EIO; 1019 } 1020 /* read base addresses */ 1021 pci_iobase = pci_resource_start(pdev, 1); 1022 iobase = pci_resource_start(pdev, 2); 1023 printk("pcibase 0x%lx iobase 0x%lx\n", pci_iobase, iobase); 1024 devpriv->pci_iobase = pci_iobase; 1025#if 0 1026/* We could enable to pci-das08's interrupt here to make it possible 1027 * to do timed input in this driver, but there is little point since 1028 * conversions would have to be started by the interrupt handler 1029 * so you might as well use comedi_rt_timer to emulate commands 1030 */ 1031 /* set source of interrupt trigger to counter2 output */ 1032 outb(CNTRL_INTR | CNTRL_DIR, pci_iobase + CNTRL); 1033 /* Enable local interrupt 1 and pci interrupt */ 1034 outw(INTR1_ENABLE | PCI_INTR_ENABLE, pci_iobase + INTCSR); 1035#endif 1036#else /* CONFIG_COMEDI_PCI */ 1037 printk("this driver has not been built with PCI support.\n"); 1038 return -EINVAL; 1039#endif /* CONFIG_COMEDI_PCI */ 1040 } else { 1041 iobase = it->options[0]; 1042 } 1043 printk("\n"); 1044 1045 return das08_common_attach(dev, iobase); 1046} 1047 1048int das08_common_detach(struct comedi_device *dev) 1049{ 1050 printk(KERN_INFO "comedi%d: das08: remove\n", dev->minor); 1051 1052 if (dev->subdevices) 1053 subdev_8255_cleanup(dev, dev->subdevices + 4); 1054 1055 /* deallocate ioports for non-pcmcia, non-pci boards */ 1056 if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) { 1057 if (dev->iobase) 1058 release_region(dev->iobase, thisboard->iosize); 1059 } 1060#ifdef CONFIG_COMEDI_PCI 1061 if (devpriv) { 1062 if (devpriv->pdev) { 1063 if (devpriv->pci_iobase) { 1064 comedi_pci_disable(devpriv->pdev); 1065 } 1066 pci_dev_put(devpriv->pdev); 1067 } 1068 } 1069#endif 1070 1071 return 0; 1072} 1073 1074#ifdef CONFIG_COMEDI_PCI 1075COMEDI_PCI_INITCLEANUP(driver_das08, das08_pci_table); 1076#else 1077COMEDI_INITCLEANUP(driver_das08); 1078#endif 1079 1080EXPORT_SYMBOL_GPL(das08_common_attach); 1081EXPORT_SYMBOL_GPL(das08_common_detach); 1082#ifdef CONFIG_COMEDI_PCMCIA 1083EXPORT_SYMBOL_GPL(das08_cs_boards); 1084#endif 1085