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