das08.c revision 389cd417e3b0f707ecd933b8fd26e910898ac2fc
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 { 510 PCI_VENDOR_ID_COMPUTERBOARDS, PCI_DEVICE_ID_PCIDAS08, 511 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { 512 0} 513}; 514 515MODULE_DEVICE_TABLE(pci, das08_pci_table); 516#endif 517 518#define devpriv ((struct das08_private_struct *)dev->private) 519#define thisboard ((const struct das08_board_struct *)dev->board_ptr) 520 521#define TIMEOUT 100000 522 523static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 524 struct comedi_insn *insn, unsigned int *data) 525{ 526 int i, n; 527 int chan; 528 int range; 529 int lsb, msb; 530 531 chan = CR_CHAN(insn->chanspec); 532 range = CR_RANGE(insn->chanspec); 533 534 /* clear crap */ 535 inb(dev->iobase + DAS08_LSB); 536 inb(dev->iobase + DAS08_MSB); 537 538 /* set multiplexer */ 539 /* lock to prevent race with digital output */ 540 spin_lock(&dev->spinlock); 541 devpriv->do_mux_bits &= ~DAS08_MUX_MASK; 542 devpriv->do_mux_bits |= DAS08_MUX(chan); 543 outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL); 544 spin_unlock(&dev->spinlock); 545 546 if (s->range_table->length > 1) { 547 /* set gain/range */ 548 range = CR_RANGE(insn->chanspec); 549 outb(devpriv->pg_gainlist[range], 550 dev->iobase + DAS08AO_GAIN_CONTROL); 551 } 552 553 for (n = 0; n < insn->n; n++) { 554 /* clear over-range bits for 16-bit boards */ 555 if (thisboard->ai_nbits == 16) 556 if (inb(dev->iobase + DAS08_MSB) & 0x80) 557 printk(KERN_INFO "das08: over-range\n"); 558 559 /* trigger conversion */ 560 outb_p(0, dev->iobase + DAS08_TRIG_12BIT); 561 562 for (i = 0; i < TIMEOUT; i++) { 563 if (!(inb(dev->iobase + DAS08_STATUS) & DAS08_EOC)) 564 break; 565 } 566 if (i == TIMEOUT) { 567 printk(KERN_ERR "das08: timeout\n"); 568 return -ETIME; 569 } 570 msb = inb(dev->iobase + DAS08_MSB); 571 lsb = inb(dev->iobase + DAS08_LSB); 572 if (thisboard->ai_encoding == das08_encode12) { 573 data[n] = (lsb >> 4) | (msb << 4); 574 } else if (thisboard->ai_encoding == das08_pcm_encode12) { 575 data[n] = (msb << 8) + lsb; 576 } else if (thisboard->ai_encoding == das08_encode16) { 577 /* FPOS 16-bit boards are sign-magnitude */ 578 if (msb & 0x80) 579 data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8); 580 else 581 data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8); 582 } else { 583 comedi_error(dev, "bug! unknown ai encoding"); 584 return -1; 585 } 586 } 587 588 return n; 589} 590 591static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s, 592 struct comedi_insn *insn, unsigned int *data) 593{ 594 data[0] = 0; 595 data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS)); 596 597 return 2; 598} 599 600static int das08_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s, 601 struct comedi_insn *insn, unsigned int *data) 602{ 603 int wbits; 604 605 /* get current settings of digital output lines */ 606 wbits = (devpriv->do_mux_bits >> 4) & 0xf; 607 /* null bits we are going to set */ 608 wbits &= ~data[0]; 609 /* set new bit values */ 610 wbits |= data[0] & data[1]; 611 /* remember digital output bits */ 612 /* prevent race with setting of analog input mux */ 613 spin_lock(&dev->spinlock); 614 devpriv->do_mux_bits &= ~DAS08_DO_MASK; 615 devpriv->do_mux_bits |= DAS08_OP(wbits); 616 outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL); 617 spin_unlock(&dev->spinlock); 618 619 data[1] = wbits; 620 621 return 2; 622} 623 624static int das08jr_di_rbits(struct comedi_device *dev, 625 struct comedi_subdevice *s, 626 struct comedi_insn *insn, unsigned int *data) 627{ 628 data[0] = 0; 629 data[1] = inb(dev->iobase + DAS08JR_DIO); 630 631 return 2; 632} 633 634static int das08jr_do_wbits(struct comedi_device *dev, 635 struct comedi_subdevice *s, 636 struct comedi_insn *insn, unsigned int *data) 637{ 638 /* null bits we are going to set */ 639 devpriv->do_bits &= ~data[0]; 640 /* set new bit values */ 641 devpriv->do_bits |= data[0] & data[1]; 642 outb(devpriv->do_bits, dev->iobase + DAS08JR_DIO); 643 644 data[1] = devpriv->do_bits; 645 646 return 2; 647} 648 649static int das08jr_ao_winsn(struct comedi_device *dev, 650 struct comedi_subdevice *s, 651 struct comedi_insn *insn, unsigned int *data) 652{ 653 int n; 654 int lsb, msb; 655 int chan; 656 657 lsb = data[0] & 0xff; 658 msb = (data[0] >> 8) & 0xf; 659 660 chan = CR_CHAN(insn->chanspec); 661 662 for (n = 0; n < insn->n; n++) { 663#if 0 664 outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]); 665 outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]); 666#else 667 outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan)); 668 outb(msb, dev->iobase + DAS08JR_AO_MSB(chan)); 669#endif 670 671 /* load DACs */ 672 inb(dev->iobase + DAS08JR_DIO); 673 } 674 675 return n; 676} 677 678/* 679 * 680 * The -aox boards have the DACs at a different offset and use 681 * a different method to force an update. 682 * 683 */ 684static int das08ao_ao_winsn(struct comedi_device *dev, 685 struct comedi_subdevice *s, 686 struct comedi_insn *insn, unsigned int *data) 687{ 688 int n; 689 int lsb, msb; 690 int chan; 691 692 lsb = data[0] & 0xff; 693 msb = (data[0] >> 8) & 0xf; 694 695 chan = CR_CHAN(insn->chanspec); 696 697 for (n = 0; n < insn->n; n++) { 698#if 0 699 outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]); 700 outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]); 701#else 702 outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan)); 703 outb(msb, dev->iobase + DAS08AO_AO_MSB(chan)); 704#endif 705 706 /* load DACs */ 707 inb(dev->iobase + DAS08AO_AO_UPDATE); 708 } 709 710 return n; 711} 712 713static unsigned int i8254_read_channel_low(unsigned int base, int chan) 714{ 715 unsigned int msb, lsb; 716 717 /* The following instructions must be in order. 718 We must avoid other process reading the counter's value in the 719 middle. 720 The spin_lock isn't needed since ioctl calls grab the big kernel 721 lock automatically */ 722 /*spin_lock(sp); */ 723 outb(chan << 6, base + I8254_CTRL); 724 base += chan; 725 lsb = inb(base); 726 msb = inb(base); 727 /*spin_unlock(sp); */ 728 729 return lsb | (msb << 8); 730} 731 732static void i8254_write_channel_low(unsigned int base, int chan, 733 unsigned int value) 734{ 735 unsigned int msb, lsb; 736 737 lsb = value & 0xFF; 738 msb = value >> 8; 739 740 /* write lsb, then msb */ 741 base += chan; 742 /* See comments in i8254_read_channel_low */ 743 /*spin_lock(sp); */ 744 outb(lsb, base); 745 outb(msb, base); 746 /*spin_unlock(sp); */ 747} 748 749static unsigned int i8254_read_channel(struct i8254_struct *st, int channel) 750{ 751 int chan = st->logic2phys[channel]; 752 753 return i8254_read_channel_low(st->iobase, chan); 754} 755 756static void i8254_write_channel(struct i8254_struct *st, int channel, 757 unsigned int value) 758{ 759 int chan = st->logic2phys[channel]; 760 761 i8254_write_channel_low(st->iobase, chan, value); 762} 763 764static void i8254_initialize(struct i8254_struct *st) 765{ 766 int i; 767 for (i = 0; i < 3; ++i) 768 i8254_set_mode_low(st->iobase, i, st->mode[i]); 769} 770 771static void i8254_set_mode_low(unsigned int base, int channel, 772 unsigned int mode) 773{ 774 outb((channel << 6) | 0x30 | (mode & 0x0F), base + I8254_CTRL); 775} 776 777static void i8254_set_mode(struct i8254_struct *st, int channel, 778 unsigned int mode) 779{ 780 int chan = st->logic2phys[channel]; 781 782 st->mode[chan] = mode; 783 return i8254_set_mode_low(st->iobase, chan, mode); 784} 785 786static unsigned int i8254_read_status_low(unsigned int base, int channel) 787{ 788 outb(0xE0 | (2 << channel), base + I8254_CTRL); 789 return inb(base + channel); 790} 791 792static unsigned int i8254_read_status(struct i8254_struct *st, int channel) 793{ 794 int chan = st->logic2phys[channel]; 795 796 return i8254_read_status_low(st->iobase, chan); 797} 798 799static int das08_counter_read(struct comedi_device *dev, 800 struct comedi_subdevice *s, 801 struct comedi_insn *insn, unsigned int *data) 802{ 803 int chan = insn->chanspec; 804 805 /* printk("Reading counter channel %d ",chan); */ 806 data[0] = i8254_read_channel(&devpriv->i8254, chan); 807 /* printk("=> 0x%08X\n",data[0]); */ 808 809 return 1; 810} 811 812static int das08_counter_write(struct comedi_device *dev, 813 struct comedi_subdevice *s, 814 struct comedi_insn *insn, unsigned int *data) 815{ 816 int chan = insn->chanspec; 817 818 /* printk("Writing counter channel %d with 0x%04X\n",chan,data[0]); */ 819 i8254_write_channel(&devpriv->i8254, chan, data[0]); 820 821 return 1; 822} 823 824static int das08_counter_config(struct comedi_device *dev, 825 struct comedi_subdevice *s, 826 struct comedi_insn *insn, unsigned int *data) 827{ 828 int chan = insn->chanspec; 829 830 if (insn->n != 2) 831 return -EINVAL; 832 833 switch (data[0]) { 834 case INSN_CONFIG_SET_COUNTER_MODE: 835 i8254_set_mode(&devpriv->i8254, chan, data[1]); 836 break; 837 case INSN_CONFIG_8254_READ_STATUS: 838 data[1] = i8254_read_status(&devpriv->i8254, chan); 839 break; 840 default: 841 return -EINVAL; 842 break; 843 } 844 return 2; 845} 846 847static int das08_attach(struct comedi_device *dev, struct comedi_devconfig *it); 848 849static struct comedi_driver driver_das08 = { 850 .driver_name = DRV_NAME, 851 .module = THIS_MODULE, 852 .attach = das08_attach, 853 .detach = das08_common_detach, 854 .board_name = &das08_boards[0].name, 855 .num_names = sizeof(das08_boards) / sizeof(struct das08_board_struct), 856 .offset = sizeof(struct das08_board_struct), 857}; 858 859int das08_common_attach(struct comedi_device *dev, unsigned long iobase) 860{ 861 struct comedi_subdevice *s; 862 int ret; 863 864 /* allocate ioports for non-pcmcia, non-pci boards */ 865 if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) { 866 printk(KERN_INFO " iobase 0x%lx\n", iobase); 867 if (!request_region(iobase, thisboard->iosize, DRV_NAME)) { 868 printk(KERN_ERR " I/O port conflict\n"); 869 return -EIO; 870 } 871 } 872 dev->iobase = iobase; 873 874 dev->board_name = thisboard->name; 875 876 ret = alloc_subdevices(dev, 6); 877 if (ret < 0) 878 return ret; 879 880 s = dev->subdevices + 0; 881 /* ai */ 882 if (thisboard->ai) { 883 s->type = COMEDI_SUBD_AI; 884 /* XXX some boards actually have differential 885 * inputs instead of single ended. 886 * The driver does nothing with arefs though, 887 * so it's no big deal. 888 */ 889 s->subdev_flags = SDF_READABLE | SDF_GROUND; 890 s->n_chan = 8; 891 s->maxdata = (1 << thisboard->ai_nbits) - 1; 892 s->range_table = das08_ai_lranges[thisboard->ai_pg]; 893 s->insn_read = thisboard->ai; 894 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg]; 895 } else { 896 s->type = COMEDI_SUBD_UNUSED; 897 } 898 899 s = dev->subdevices + 1; 900 /* ao */ 901 if (thisboard->ao) { 902 s->type = COMEDI_SUBD_AO; 903/* XXX lacks read-back insn */ 904 s->subdev_flags = SDF_WRITABLE; 905 s->n_chan = 2; 906 s->maxdata = (1 << thisboard->ao_nbits) - 1; 907 s->range_table = &range_bipolar5; 908 s->insn_write = thisboard->ao; 909 } else { 910 s->type = COMEDI_SUBD_UNUSED; 911 } 912 913 s = dev->subdevices + 2; 914 /* di */ 915 if (thisboard->di) { 916 s->type = COMEDI_SUBD_DI; 917 s->subdev_flags = SDF_READABLE; 918 s->n_chan = (thisboard->di == das08_di_rbits) ? 3 : 8; 919 s->maxdata = 1; 920 s->range_table = &range_digital; 921 s->insn_bits = thisboard->di; 922 } else { 923 s->type = COMEDI_SUBD_UNUSED; 924 } 925 926 s = dev->subdevices + 3; 927 /* do */ 928 if (thisboard->do_) { 929 s->type = COMEDI_SUBD_DO; 930 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 931 s->n_chan = thisboard->do_nchan; 932 s->maxdata = 1; 933 s->range_table = &range_digital; 934 s->insn_bits = thisboard->do_; 935 } else { 936 s->type = COMEDI_SUBD_UNUSED; 937 } 938 939 s = dev->subdevices + 4; 940 /* 8255 */ 941 if (thisboard->i8255_offset != 0) { 942 subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase + 943 thisboard-> 944 i8255_offset)); 945 } else { 946 s->type = COMEDI_SUBD_UNUSED; 947 } 948 949 s = dev->subdevices + 5; 950 /* 8254 */ 951 if (thisboard->i8254_offset != 0) { 952 s->type = COMEDI_SUBD_COUNTER; 953 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 954 s->n_chan = 3; 955 s->maxdata = 0xFFFF; 956 s->insn_read = das08_counter_read; 957 s->insn_write = das08_counter_write; 958 s->insn_config = das08_counter_config; 959 /* Set-up the 8254 structure */ 960 devpriv->i8254.channels = 3; 961 devpriv->i8254.logic2phys[0] = 0; 962 devpriv->i8254.logic2phys[1] = 1; 963 devpriv->i8254.logic2phys[2] = 2; 964 devpriv->i8254.iobase = iobase + thisboard->i8254_offset; 965 devpriv->i8254.mode[0] = 966 devpriv->i8254.mode[1] = 967 devpriv->i8254.mode[2] = I8254_MODE0 | I8254_BINARY; 968 i8254_initialize(&devpriv->i8254); 969 } else { 970 s->type = COMEDI_SUBD_UNUSED; 971 } 972 973 return 0; 974} 975EXPORT_SYMBOL_GPL(das08_common_attach); 976 977static int das08_attach(struct comedi_device *dev, struct comedi_devconfig *it) 978{ 979 int ret; 980 unsigned long iobase; 981#ifdef CONFIG_COMEDI_PCI 982 unsigned long pci_iobase = 0; 983 struct pci_dev *pdev; 984#endif 985 986 ret = alloc_private(dev, sizeof(struct das08_private_struct)); 987 if (ret < 0) 988 return ret; 989 990 printk(KERN_INFO "comedi%d: das08: ", dev->minor); 991 /* deal with a pci board */ 992 if (thisboard->bustype == pci) { 993#ifdef CONFIG_COMEDI_PCI 994 if (it->options[0] || it->options[1]) { 995 printk("bus %i slot %i ", 996 it->options[0], it->options[1]); 997 } 998 printk("\n"); 999 /* find card */ 1000 for (pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); 1001 pdev != NULL; 1002 pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) { 1003 if (pdev->vendor == PCI_VENDOR_ID_COMPUTERBOARDS 1004 && pdev->device == PCI_DEVICE_ID_PCIDAS08) { 1005 if (it->options[0] || it->options[1]) { 1006 if (pdev->bus->number == it->options[0] 1007 && PCI_SLOT(pdev->devfn) == 1008 it->options[1]) { 1009 break; 1010 } 1011 } else { 1012 break; 1013 } 1014 } 1015 } 1016 if (!pdev) { 1017 printk(KERN_ERR "No pci das08 cards found\n"); 1018 return -EIO; 1019 } 1020 devpriv->pdev = pdev; 1021 /* enable PCI device and reserve I/O spaces */ 1022 if (comedi_pci_enable(pdev, DRV_NAME)) { 1023 printk(KERN_ERR " Error enabling PCI device and " 1024 "requesting regions\n"); 1025 return -EIO; 1026 } 1027 /* read base addresses */ 1028 pci_iobase = pci_resource_start(pdev, 1); 1029 iobase = pci_resource_start(pdev, 2); 1030 printk(KERN_INFO "pcibase 0x%lx iobase 0x%lx\n", 1031 pci_iobase, iobase); 1032 devpriv->pci_iobase = pci_iobase; 1033#if 0 1034/* We could enable to pci-das08's interrupt here to make it possible 1035 * to do timed input in this driver, but there is little point since 1036 * conversions would have to be started by the interrupt handler 1037 * so you might as well use comedi_rt_timer to emulate commands 1038 */ 1039 /* set source of interrupt trigger to counter2 output */ 1040 outb(CNTRL_INTR | CNTRL_DIR, pci_iobase + CNTRL); 1041 /* Enable local interrupt 1 and pci interrupt */ 1042 outw(INTR1_ENABLE | PCI_INTR_ENABLE, pci_iobase + INTCSR); 1043#endif 1044#else /* CONFIG_COMEDI_PCI */ 1045 printk(KERN_ERR "this driver has not been built with PCI support.\n"); 1046 return -EINVAL; 1047#endif /* CONFIG_COMEDI_PCI */ 1048 } else { 1049 iobase = it->options[0]; 1050 } 1051 printk(KERN_INFO "\n"); 1052 1053 return das08_common_attach(dev, iobase); 1054} 1055 1056 1057int das08_common_detach(struct comedi_device *dev) 1058{ 1059 printk(KERN_INFO "comedi%d: das08: remove\n", dev->minor); 1060 1061 if (dev->subdevices) 1062 subdev_8255_cleanup(dev, dev->subdevices + 4); 1063 1064 /* deallocate ioports for non-pcmcia, non-pci boards */ 1065 if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) { 1066 if (dev->iobase) 1067 release_region(dev->iobase, thisboard->iosize); 1068 } 1069#ifdef CONFIG_COMEDI_PCI 1070 if (devpriv) { 1071 if (devpriv->pdev) { 1072 if (devpriv->pci_iobase) 1073 comedi_pci_disable(devpriv->pdev); 1074 1075 pci_dev_put(devpriv->pdev); 1076 } 1077 } 1078#endif 1079 1080 return 0; 1081} 1082EXPORT_SYMBOL_GPL(das08_common_detach); 1083 1084#ifdef CONFIG_COMEDI_PCI 1085static int __devinit driver_das08_pci_probe(struct pci_dev *dev, 1086 const struct pci_device_id *ent) 1087{ 1088 return comedi_pci_auto_config(dev, driver_das08.driver_name); 1089} 1090 1091static void __devexit driver_das08_pci_remove(struct pci_dev *dev) 1092{ 1093 comedi_pci_auto_unconfig(dev); 1094} 1095 1096static struct pci_driver driver_das08_pci_driver = { 1097 .id_table = das08_pci_table, 1098 .probe = &driver_das08_pci_probe, 1099 .remove = __devexit_p(&driver_das08_pci_remove) 1100}; 1101 1102static int __init driver_das08_init_module(void) 1103{ 1104 int retval; 1105 1106 retval = comedi_driver_register(&driver_das08); 1107 if (retval < 0) 1108 return retval; 1109 1110 driver_das08_pci_driver.name = (char *)driver_das08.driver_name; 1111 return pci_register_driver(&driver_das08_pci_driver); 1112} 1113 1114static void __exit driver_das08_cleanup_module(void) 1115{ 1116 pci_unregister_driver(&driver_das08_pci_driver); 1117 comedi_driver_unregister(&driver_das08); 1118} 1119 1120module_init(driver_das08_init_module); 1121module_exit(driver_das08_cleanup_module); 1122#else 1123static int __init driver_das08_init_module(void) 1124{ 1125 return comedi_driver_register(&driver_das08); 1126} 1127 1128static void __exit driver_das08_cleanup_module(void) 1129{ 1130 comedi_driver_unregister(&driver_das08); 1131} 1132 1133module_init(driver_das08_init_module); 1134module_exit(driver_das08_cleanup_module); 1135#endif 1136 1137#ifdef CONFIG_COMEDI_PCMCIA 1138EXPORT_SYMBOL_GPL(das08_cs_boards); 1139#endif 1140 1141MODULE_AUTHOR("Comedi http://www.comedi.org"); 1142MODULE_DESCRIPTION("Comedi low-level driver"); 1143MODULE_LICENSE("GPL"); 1144