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/* 28 * Driver: das08 29 * Description: DAS-08 compatible boards 30 * Author: Warren Jasper, ds, Frank Hess 31 * Devices: [Keithley Metrabyte] DAS08 (isa-das08), 32 * [ComputerBoards] DAS08 (isa-das08), 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) 37 * Status: works 38 * 39 * This is a rewrite of the das08 and das08jr drivers. 40 * 41 * Options (for ISA cards): 42 * [0] - base io address 43 * 44 * Options (for pci-das08): 45 * [0] - bus (optional) 46 * [1] = slot (optional) 47 * 48 * The das08 driver doesn't support asynchronous commands, since 49 * the cheap das08 hardware doesn't really support them. The 50 * comedi_rt_timer driver can be used to emulate commands for this 51 * driver. 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 244}; 245static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 }; 246static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 }; 247 248static const int *const das08_gainlists[] = { 249 NULL, 250 NULL, 251 das08_pgh_gainlist, 252 das08_pgl_gainlist, 253 das08_pgm_gainlist, 254}; 255 256static const struct das08_board_struct das08_boards[] = { 257 { 258 .name = "isa-das08", /* cio-das08.pdf */ 259 .bustype = isa, 260 .ai = das08_ai_rinsn, 261 .ai_nbits = 12, 262 .ai_pg = das08_pg_none, 263 .ai_encoding = das08_encode12, 264 .ao = NULL, 265 .ao_nbits = 12, 266 .di = das08_di_rbits, 267 .do_ = das08_do_wbits, 268 .do_nchan = 4, 269 .i8255_offset = 8, 270 .i8254_offset = 4, 271 .iosize = 16, /* unchecked */ 272 }, 273 { 274 .name = "das08-pgm", /* cio-das08pgx.pdf */ 275 .bustype = isa, 276 .ai = das08_ai_rinsn, 277 .ai_nbits = 12, 278 .ai_pg = das08_pgm, 279 .ai_encoding = das08_encode12, 280 .ao = NULL, 281 .di = das08_di_rbits, 282 .do_ = das08_do_wbits, 283 .do_nchan = 4, 284 .i8255_offset = 0, 285 .i8254_offset = 0x04, 286 .iosize = 16, /* unchecked */ 287 }, 288 { 289 .name = "das08-pgh", /* cio-das08pgx.pdf */ 290 .bustype = isa, 291 .ai = das08_ai_rinsn, 292 .ai_nbits = 12, 293 .ai_pg = das08_pgh, 294 .ai_encoding = das08_encode12, 295 .ao = NULL, 296 .di = das08_di_rbits, 297 .do_ = das08_do_wbits, 298 .do_nchan = 4, 299 .i8255_offset = 0, 300 .i8254_offset = 0x04, 301 .iosize = 16, /* unchecked */ 302 }, 303 { 304 .name = "das08-pgl", /* cio-das08pgx.pdf */ 305 .bustype = isa, 306 .ai = das08_ai_rinsn, 307 .ai_nbits = 12, 308 .ai_pg = das08_pgl, 309 .ai_encoding = das08_encode12, 310 .ao = NULL, 311 .di = das08_di_rbits, 312 .do_ = das08_do_wbits, 313 .do_nchan = 4, 314 .i8255_offset = 0, 315 .i8254_offset = 0x04, 316 .iosize = 16, /* unchecked */ 317 }, 318 { 319 .name = "das08-aoh", /* cio-das08_aox.pdf */ 320 .bustype = isa, 321 .ai = das08_ai_rinsn, 322 .ai_nbits = 12, 323 .ai_pg = das08_pgh, 324 .ai_encoding = das08_encode12, 325 .ao = das08ao_ao_winsn, /* 8 */ 326 .ao_nbits = 12, 327 .di = das08_di_rbits, 328 .do_ = das08_do_wbits, 329 .do_nchan = 4, 330 .i8255_offset = 0x0c, 331 .i8254_offset = 0x04, 332 .iosize = 16, /* unchecked */ 333 }, 334 { 335 .name = "das08-aol", /* cio-das08_aox.pdf */ 336 .bustype = isa, 337 .ai = das08_ai_rinsn, 338 .ai_nbits = 12, 339 .ai_pg = das08_pgl, 340 .ai_encoding = das08_encode12, 341 .ao = das08ao_ao_winsn, /* 8 */ 342 .ao_nbits = 12, 343 .di = das08_di_rbits, 344 .do_ = das08_do_wbits, 345 .do_nchan = 4, 346 .i8255_offset = 0x0c, 347 .i8254_offset = 0x04, 348 .iosize = 16, /* unchecked */ 349 }, 350 { 351 .name = "das08-aom", /* cio-das08_aox.pdf */ 352 .bustype = isa, 353 .ai = das08_ai_rinsn, 354 .ai_nbits = 12, 355 .ai_pg = das08_pgm, 356 .ai_encoding = das08_encode12, 357 .ao = das08ao_ao_winsn, /* 8 */ 358 .ao_nbits = 12, 359 .di = das08_di_rbits, 360 .do_ = das08_do_wbits, 361 .do_nchan = 4, 362 .i8255_offset = 0x0c, 363 .i8254_offset = 0x04, 364 .iosize = 16, /* unchecked */ 365 }, 366 { 367 .name = "das08/jr-ao", /* cio-das08-jr-ao.pdf */ 368 .bustype = isa, 369 .ai = das08_ai_rinsn, 370 .ai_nbits = 12, 371 .ai_pg = das08_pg_none, 372 .ai_encoding = das08_encode12, 373 .ao = das08jr_ao_winsn, 374 .ao_nbits = 12, 375 .di = das08jr_di_rbits, 376 .do_ = das08jr_do_wbits, 377 .do_nchan = 8, 378 .i8255_offset = 0, 379 .i8254_offset = 0, 380 .iosize = 16, /* unchecked */ 381 }, 382 { 383 .name = "das08jr-16-ao", /* cio-das08jr-16-ao.pdf */ 384 .bustype = isa, 385 .ai = das08_ai_rinsn, 386 .ai_nbits = 16, 387 .ai_pg = das08_pg_none, 388 .ai_encoding = das08_encode12, 389 .ao = das08jr_ao_winsn, 390 .ao_nbits = 16, 391 .di = das08jr_di_rbits, 392 .do_ = das08jr_do_wbits, 393 .do_nchan = 8, 394 .i8255_offset = 0, 395 .i8254_offset = 0x04, 396 .iosize = 16, /* unchecked */ 397 }, 398#ifdef CONFIG_COMEDI_PCI 399 { 400 .name = "das08", /* pci-das08 */ 401 .id = PCI_DEVICE_ID_PCIDAS08, 402 .bustype = pci, 403 .ai = das08_ai_rinsn, 404 .ai_nbits = 12, 405 .ai_pg = das08_bipolar5, 406 .ai_encoding = das08_encode12, 407 .ao = NULL, 408 .ao_nbits = 0, 409 .di = das08_di_rbits, 410 .do_ = das08_do_wbits, 411 .do_nchan = 4, 412 .i8255_offset = 0, 413 .i8254_offset = 4, 414 .iosize = 8, 415 }, 416#endif 417 { 418 .name = "pc104-das08", 419 .bustype = pc104, 420 .ai = das08_ai_rinsn, 421 .ai_nbits = 12, 422 .ai_pg = das08_pg_none, 423 .ai_encoding = das08_encode12, 424 .ao = NULL, 425 .ao_nbits = 0, 426 .di = das08_di_rbits, 427 .do_ = das08_do_wbits, 428 .do_nchan = 4, 429 .i8255_offset = 0, 430 .i8254_offset = 4, 431 .iosize = 16, /* unchecked */ 432 }, 433#if 0 434 { 435 .name = "das08/f", 436 }, 437 { 438 .name = "das08jr", 439 }, 440#endif 441 { 442 .name = "das08jr/16", 443 .bustype = isa, 444 .ai = das08_ai_rinsn, 445 .ai_nbits = 16, 446 .ai_pg = das08_pg_none, 447 .ai_encoding = das08_encode16, 448 .ao = NULL, 449 .ao_nbits = 0, 450 .di = das08jr_di_rbits, 451 .do_ = das08jr_do_wbits, 452 .do_nchan = 8, 453 .i8255_offset = 0, 454 .i8254_offset = 0, 455 .iosize = 16, /* unchecked */ 456 }, 457#if 0 458 { 459 .name = "das48-pga", /* cio-das48-pga.pdf */ 460 }, 461 { 462 .name = "das08-pga-g2", /* a KM board */ 463 }, 464#endif 465}; 466 467#ifdef CONFIG_COMEDI_PCMCIA 468struct das08_board_struct das08_cs_boards[NUM_DAS08_CS_BOARDS] = { 469 { 470 .name = "pcm-das08", 471 .id = 0x0, /* XXX */ 472 .bustype = pcmcia, 473 .ai = das08_ai_rinsn, 474 .ai_nbits = 12, 475 .ai_pg = das08_bipolar5, 476 .ai_encoding = das08_pcm_encode12, 477 .ao = NULL, 478 .ao_nbits = 0, 479 .di = das08_di_rbits, 480 .do_ = das08_do_wbits, 481 .do_nchan = 3, 482 .i8255_offset = 0, 483 .i8254_offset = 0, 484 .iosize = 16, 485 }, 486 /* duplicate so driver name can be used also */ 487 { 488 .name = "das08_cs", 489 .id = 0x0, /* XXX */ 490 .bustype = pcmcia, 491 .ai = das08_ai_rinsn, 492 .ai_nbits = 12, 493 .ai_pg = das08_bipolar5, 494 .ai_encoding = das08_pcm_encode12, 495 .ao = NULL, 496 .ao_nbits = 0, 497 .di = das08_di_rbits, 498 .do_ = das08_do_wbits, 499 .do_nchan = 3, 500 .i8255_offset = 0, 501 .i8254_offset = 0, 502 .iosize = 16, 503 }, 504}; 505#endif 506 507#ifdef CONFIG_COMEDI_PCI 508static DEFINE_PCI_DEVICE_TABLE(das08_pci_table) = { 509 { PCI_DEVICE(PCI_VENDOR_ID_COMPUTERBOARDS, PCI_DEVICE_ID_PCIDAS08) }, 510 {0} 511}; 512 513MODULE_DEVICE_TABLE(pci, das08_pci_table); 514#endif 515 516#define devpriv ((struct das08_private_struct *)dev->private) 517#define thisboard ((const struct das08_board_struct *)dev->board_ptr) 518 519#define TIMEOUT 100000 520 521static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 522 struct comedi_insn *insn, unsigned int *data) 523{ 524 int i, n; 525 int chan; 526 int range; 527 int lsb, msb; 528 529 chan = CR_CHAN(insn->chanspec); 530 range = CR_RANGE(insn->chanspec); 531 532 /* clear crap */ 533 inb(dev->iobase + DAS08_LSB); 534 inb(dev->iobase + DAS08_MSB); 535 536 /* set multiplexer */ 537 /* lock to prevent race with digital output */ 538 spin_lock(&dev->spinlock); 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(KERN_INFO "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(KERN_ERR "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 /* prevent race with setting of analog input mux */ 611 spin_lock(&dev->spinlock); 612 devpriv->do_mux_bits &= ~DAS08_DO_MASK; 613 devpriv->do_mux_bits |= DAS08_OP(wbits); 614 outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL); 615 spin_unlock(&dev->spinlock); 616 617 data[1] = wbits; 618 619 return 2; 620} 621 622static int das08jr_di_rbits(struct comedi_device *dev, 623 struct comedi_subdevice *s, 624 struct comedi_insn *insn, unsigned int *data) 625{ 626 data[0] = 0; 627 data[1] = inb(dev->iobase + DAS08JR_DIO); 628 629 return 2; 630} 631 632static int das08jr_do_wbits(struct comedi_device *dev, 633 struct comedi_subdevice *s, 634 struct comedi_insn *insn, unsigned int *data) 635{ 636 /* null bits we are going to set */ 637 devpriv->do_bits &= ~data[0]; 638 /* set new bit values */ 639 devpriv->do_bits |= data[0] & data[1]; 640 outb(devpriv->do_bits, dev->iobase + DAS08JR_DIO); 641 642 data[1] = devpriv->do_bits; 643 644 return 2; 645} 646 647static int das08jr_ao_winsn(struct comedi_device *dev, 648 struct comedi_subdevice *s, 649 struct comedi_insn *insn, unsigned int *data) 650{ 651 int n; 652 int lsb, msb; 653 int chan; 654 655 lsb = data[0] & 0xff; 656 msb = (data[0] >> 8) & 0xf; 657 658 chan = CR_CHAN(insn->chanspec); 659 660 for (n = 0; n < insn->n; n++) { 661#if 0 662 outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]); 663 outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]); 664#else 665 outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan)); 666 outb(msb, dev->iobase + DAS08JR_AO_MSB(chan)); 667#endif 668 669 /* load DACs */ 670 inb(dev->iobase + DAS08JR_DIO); 671 } 672 673 return n; 674} 675 676/* 677 * 678 * The -aox boards have the DACs at a different offset and use 679 * a different method to force an update. 680 * 681 */ 682static int das08ao_ao_winsn(struct comedi_device *dev, 683 struct comedi_subdevice *s, 684 struct comedi_insn *insn, unsigned int *data) 685{ 686 int n; 687 int lsb, msb; 688 int chan; 689 690 lsb = data[0] & 0xff; 691 msb = (data[0] >> 8) & 0xf; 692 693 chan = CR_CHAN(insn->chanspec); 694 695 for (n = 0; n < insn->n; n++) { 696#if 0 697 outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]); 698 outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]); 699#else 700 outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan)); 701 outb(msb, dev->iobase + DAS08AO_AO_MSB(chan)); 702#endif 703 704 /* load DACs */ 705 inb(dev->iobase + DAS08AO_AO_UPDATE); 706 } 707 708 return n; 709} 710 711static unsigned int i8254_read_channel_low(unsigned int base, int chan) 712{ 713 unsigned int msb, lsb; 714 715 /* The following instructions must be in order. 716 We must avoid other process reading the counter's value in the 717 middle. 718 The spin_lock isn't needed since ioctl calls grab the big kernel 719 lock automatically */ 720 /*spin_lock(sp); */ 721 outb(chan << 6, base + I8254_CTRL); 722 base += chan; 723 lsb = inb(base); 724 msb = inb(base); 725 /*spin_unlock(sp); */ 726 727 return lsb | (msb << 8); 728} 729 730static void i8254_write_channel_low(unsigned int base, int chan, 731 unsigned int value) 732{ 733 unsigned int msb, lsb; 734 735 lsb = value & 0xFF; 736 msb = value >> 8; 737 738 /* write lsb, then msb */ 739 base += chan; 740 /* See comments in i8254_read_channel_low */ 741 /*spin_lock(sp); */ 742 outb(lsb, base); 743 outb(msb, base); 744 /*spin_unlock(sp); */ 745} 746 747static unsigned int i8254_read_channel(struct i8254_struct *st, int channel) 748{ 749 int chan = st->logic2phys[channel]; 750 751 return i8254_read_channel_low(st->iobase, chan); 752} 753 754static void i8254_write_channel(struct i8254_struct *st, int channel, 755 unsigned int value) 756{ 757 int chan = st->logic2phys[channel]; 758 759 i8254_write_channel_low(st->iobase, chan, value); 760} 761 762static void i8254_initialize(struct i8254_struct *st) 763{ 764 int i; 765 for (i = 0; i < 3; ++i) 766 i8254_set_mode_low(st->iobase, i, st->mode[i]); 767} 768 769static void i8254_set_mode_low(unsigned int base, int channel, 770 unsigned int mode) 771{ 772 outb((channel << 6) | 0x30 | (mode & 0x0F), base + I8254_CTRL); 773} 774 775static void i8254_set_mode(struct i8254_struct *st, int channel, 776 unsigned int mode) 777{ 778 int chan = st->logic2phys[channel]; 779 780 st->mode[chan] = mode; 781 return i8254_set_mode_low(st->iobase, chan, mode); 782} 783 784static unsigned int i8254_read_status_low(unsigned int base, int channel) 785{ 786 outb(0xE0 | (2 << channel), base + I8254_CTRL); 787 return inb(base + channel); 788} 789 790static unsigned int i8254_read_status(struct i8254_struct *st, int channel) 791{ 792 int chan = st->logic2phys[channel]; 793 794 return i8254_read_status_low(st->iobase, chan); 795} 796 797static int das08_counter_read(struct comedi_device *dev, 798 struct comedi_subdevice *s, 799 struct comedi_insn *insn, unsigned int *data) 800{ 801 int chan = insn->chanspec; 802 803 /* printk("Reading counter channel %d ",chan); */ 804 data[0] = i8254_read_channel(&devpriv->i8254, chan); 805 /* printk("=> 0x%08X\n",data[0]); */ 806 807 return 1; 808} 809 810static int das08_counter_write(struct comedi_device *dev, 811 struct comedi_subdevice *s, 812 struct comedi_insn *insn, unsigned int *data) 813{ 814 int chan = insn->chanspec; 815 816 /* printk("Writing counter channel %d with 0x%04X\n",chan,data[0]); */ 817 i8254_write_channel(&devpriv->i8254, chan, data[0]); 818 819 return 1; 820} 821 822static int das08_counter_config(struct comedi_device *dev, 823 struct comedi_subdevice *s, 824 struct comedi_insn *insn, unsigned int *data) 825{ 826 int chan = insn->chanspec; 827 828 if (insn->n != 2) 829 return -EINVAL; 830 831 switch (data[0]) { 832 case INSN_CONFIG_SET_COUNTER_MODE: 833 i8254_set_mode(&devpriv->i8254, chan, data[1]); 834 break; 835 case INSN_CONFIG_8254_READ_STATUS: 836 data[1] = i8254_read_status(&devpriv->i8254, chan); 837 break; 838 default: 839 return -EINVAL; 840 break; 841 } 842 return 2; 843} 844 845static int das08_attach(struct comedi_device *dev, struct comedi_devconfig *it); 846 847static struct comedi_driver driver_das08 = { 848 .driver_name = DRV_NAME, 849 .module = THIS_MODULE, 850 .attach = das08_attach, 851 .detach = das08_common_detach, 852 .board_name = &das08_boards[0].name, 853 .num_names = sizeof(das08_boards) / sizeof(struct das08_board_struct), 854 .offset = sizeof(struct das08_board_struct), 855}; 856 857int das08_common_attach(struct comedi_device *dev, unsigned long iobase) 858{ 859 struct comedi_subdevice *s; 860 int ret; 861 862 /* allocate ioports for non-pcmcia, non-pci boards */ 863 if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) { 864 printk(KERN_INFO " iobase 0x%lx\n", iobase); 865 if (!request_region(iobase, thisboard->iosize, DRV_NAME)) { 866 printk(KERN_ERR " I/O port conflict\n"); 867 return -EIO; 868 } 869 } 870 dev->iobase = iobase; 871 872 dev->board_name = thisboard->name; 873 874 ret = alloc_subdevices(dev, 6); 875 if (ret < 0) 876 return ret; 877 878 s = dev->subdevices + 0; 879 /* ai */ 880 if (thisboard->ai) { 881 s->type = COMEDI_SUBD_AI; 882 /* XXX some boards actually have differential 883 * inputs instead of single ended. 884 * The driver does nothing with arefs though, 885 * so it's no big deal. 886 */ 887 s->subdev_flags = SDF_READABLE | SDF_GROUND; 888 s->n_chan = 8; 889 s->maxdata = (1 << thisboard->ai_nbits) - 1; 890 s->range_table = das08_ai_lranges[thisboard->ai_pg]; 891 s->insn_read = thisboard->ai; 892 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg]; 893 } else { 894 s->type = COMEDI_SUBD_UNUSED; 895 } 896 897 s = dev->subdevices + 1; 898 /* ao */ 899 if (thisboard->ao) { 900 s->type = COMEDI_SUBD_AO; 901/* XXX lacks read-back insn */ 902 s->subdev_flags = SDF_WRITABLE; 903 s->n_chan = 2; 904 s->maxdata = (1 << thisboard->ao_nbits) - 1; 905 s->range_table = &range_bipolar5; 906 s->insn_write = thisboard->ao; 907 } else { 908 s->type = COMEDI_SUBD_UNUSED; 909 } 910 911 s = dev->subdevices + 2; 912 /* di */ 913 if (thisboard->di) { 914 s->type = COMEDI_SUBD_DI; 915 s->subdev_flags = SDF_READABLE; 916 s->n_chan = (thisboard->di == das08_di_rbits) ? 3 : 8; 917 s->maxdata = 1; 918 s->range_table = &range_digital; 919 s->insn_bits = thisboard->di; 920 } else { 921 s->type = COMEDI_SUBD_UNUSED; 922 } 923 924 s = dev->subdevices + 3; 925 /* do */ 926 if (thisboard->do_) { 927 s->type = COMEDI_SUBD_DO; 928 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 929 s->n_chan = thisboard->do_nchan; 930 s->maxdata = 1; 931 s->range_table = &range_digital; 932 s->insn_bits = thisboard->do_; 933 } else { 934 s->type = COMEDI_SUBD_UNUSED; 935 } 936 937 s = dev->subdevices + 4; 938 /* 8255 */ 939 if (thisboard->i8255_offset != 0) { 940 subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase + 941 thisboard-> 942 i8255_offset)); 943 } else { 944 s->type = COMEDI_SUBD_UNUSED; 945 } 946 947 s = dev->subdevices + 5; 948 /* 8254 */ 949 if (thisboard->i8254_offset != 0) { 950 s->type = COMEDI_SUBD_COUNTER; 951 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 952 s->n_chan = 3; 953 s->maxdata = 0xFFFF; 954 s->insn_read = das08_counter_read; 955 s->insn_write = das08_counter_write; 956 s->insn_config = das08_counter_config; 957 /* Set-up the 8254 structure */ 958 devpriv->i8254.channels = 3; 959 devpriv->i8254.logic2phys[0] = 0; 960 devpriv->i8254.logic2phys[1] = 1; 961 devpriv->i8254.logic2phys[2] = 2; 962 devpriv->i8254.iobase = iobase + thisboard->i8254_offset; 963 devpriv->i8254.mode[0] = 964 devpriv->i8254.mode[1] = 965 devpriv->i8254.mode[2] = I8254_MODE0 | I8254_BINARY; 966 i8254_initialize(&devpriv->i8254); 967 } else { 968 s->type = COMEDI_SUBD_UNUSED; 969 } 970 971 return 0; 972} 973EXPORT_SYMBOL_GPL(das08_common_attach); 974 975static int das08_attach(struct comedi_device *dev, struct comedi_devconfig *it) 976{ 977 int ret; 978 unsigned long iobase; 979#ifdef CONFIG_COMEDI_PCI 980 unsigned long pci_iobase = 0; 981 struct pci_dev *pdev = NULL; 982#endif 983 984 ret = alloc_private(dev, sizeof(struct das08_private_struct)); 985 if (ret < 0) 986 return ret; 987 988 printk(KERN_INFO "comedi%d: das08: ", dev->minor); 989 /* deal with a pci board */ 990 if (thisboard->bustype == pci) { 991#ifdef CONFIG_COMEDI_PCI 992 if (it->options[0] || it->options[1]) { 993 printk("bus %i slot %i ", 994 it->options[0], it->options[1]); 995 } 996 printk("\n"); 997 /* find card */ 998 for_each_pci_dev(pdev) { 999 if (pdev->vendor == PCI_VENDOR_ID_COMPUTERBOARDS 1000 && pdev->device == PCI_DEVICE_ID_PCIDAS08) { 1001 if (it->options[0] || it->options[1]) { 1002 if (pdev->bus->number == it->options[0] 1003 && PCI_SLOT(pdev->devfn) == 1004 it->options[1]) { 1005 break; 1006 } 1007 } else { 1008 break; 1009 } 1010 } 1011 } 1012 if (!pdev) { 1013 printk(KERN_ERR "No pci das08 cards found\n"); 1014 return -EIO; 1015 } 1016 devpriv->pdev = pdev; 1017 /* enable PCI device and reserve I/O spaces */ 1018 if (comedi_pci_enable(pdev, DRV_NAME)) { 1019 printk(KERN_ERR " Error enabling PCI device and " 1020 "requesting regions\n"); 1021 return -EIO; 1022 } 1023 /* read base addresses */ 1024 pci_iobase = pci_resource_start(pdev, 1); 1025 iobase = pci_resource_start(pdev, 2); 1026 printk(KERN_INFO "pcibase 0x%lx iobase 0x%lx\n", 1027 pci_iobase, iobase); 1028 devpriv->pci_iobase = pci_iobase; 1029#if 0 1030/* We could enable to pci-das08's interrupt here to make it possible 1031 * to do timed input in this driver, but there is little point since 1032 * conversions would have to be started by the interrupt handler 1033 * so you might as well use comedi_rt_timer to emulate commands 1034 */ 1035 /* set source of interrupt trigger to counter2 output */ 1036 outb(CNTRL_INTR | CNTRL_DIR, pci_iobase + CNTRL); 1037 /* Enable local interrupt 1 and pci interrupt */ 1038 outw(INTR1_ENABLE | PCI_INTR_ENABLE, pci_iobase + INTCSR); 1039#endif 1040#else /* CONFIG_COMEDI_PCI */ 1041 printk(KERN_ERR "this driver has not been built with PCI support.\n"); 1042 return -EINVAL; 1043#endif /* CONFIG_COMEDI_PCI */ 1044 } else { 1045 iobase = it->options[0]; 1046 } 1047 printk(KERN_INFO "\n"); 1048 1049 return das08_common_attach(dev, iobase); 1050} 1051 1052 1053int das08_common_detach(struct comedi_device *dev) 1054{ 1055 printk(KERN_INFO "comedi%d: das08: remove\n", dev->minor); 1056 1057 if (dev->subdevices) 1058 subdev_8255_cleanup(dev, dev->subdevices + 4); 1059 1060 /* deallocate ioports for non-pcmcia, non-pci boards */ 1061 if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) { 1062 if (dev->iobase) 1063 release_region(dev->iobase, thisboard->iosize); 1064 } 1065#ifdef CONFIG_COMEDI_PCI 1066 if (devpriv) { 1067 if (devpriv->pdev) { 1068 if (devpriv->pci_iobase) 1069 comedi_pci_disable(devpriv->pdev); 1070 1071 pci_dev_put(devpriv->pdev); 1072 } 1073 } 1074#endif 1075 1076 return 0; 1077} 1078EXPORT_SYMBOL_GPL(das08_common_detach); 1079 1080#ifdef CONFIG_COMEDI_PCI 1081static int __devinit driver_das08_pci_probe(struct pci_dev *dev, 1082 const struct pci_device_id *ent) 1083{ 1084 return comedi_pci_auto_config(dev, driver_das08.driver_name); 1085} 1086 1087static void __devexit driver_das08_pci_remove(struct pci_dev *dev) 1088{ 1089 comedi_pci_auto_unconfig(dev); 1090} 1091 1092static struct pci_driver driver_das08_pci_driver = { 1093 .id_table = das08_pci_table, 1094 .probe = &driver_das08_pci_probe, 1095 .remove = __devexit_p(&driver_das08_pci_remove) 1096}; 1097 1098static int __init driver_das08_init_module(void) 1099{ 1100 int retval; 1101 1102 retval = comedi_driver_register(&driver_das08); 1103 if (retval < 0) 1104 return retval; 1105 1106 driver_das08_pci_driver.name = (char *)driver_das08.driver_name; 1107 return pci_register_driver(&driver_das08_pci_driver); 1108} 1109 1110static void __exit driver_das08_cleanup_module(void) 1111{ 1112 pci_unregister_driver(&driver_das08_pci_driver); 1113 comedi_driver_unregister(&driver_das08); 1114} 1115 1116module_init(driver_das08_init_module); 1117module_exit(driver_das08_cleanup_module); 1118#else 1119static int __init driver_das08_init_module(void) 1120{ 1121 return comedi_driver_register(&driver_das08); 1122} 1123 1124static void __exit driver_das08_cleanup_module(void) 1125{ 1126 comedi_driver_unregister(&driver_das08); 1127} 1128 1129module_init(driver_das08_init_module); 1130module_exit(driver_das08_cleanup_module); 1131#endif 1132 1133#ifdef CONFIG_COMEDI_PCMCIA 1134EXPORT_SYMBOL_GPL(das08_cs_boards); 1135#endif 1136 1137MODULE_AUTHOR("Comedi http://www.comedi.org"); 1138MODULE_DESCRIPTION("Comedi low-level driver"); 1139MODULE_LICENSE("GPL"); 1140