das08.c revision da91b2692e0939b307f9047192d2b9fe07793e7a
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 if ((ret = alloc_subdevices(dev, 6)) < 0) 861 return ret; 862 863 s = dev->subdevices + 0; 864 /* ai */ 865 if (thisboard->ai) { 866 s->type = COMEDI_SUBD_AI; 867 /* XXX some boards actually have differential inputs instead of single ended. 868 * The driver does nothing with arefs though, so it's no big deal. */ 869 s->subdev_flags = SDF_READABLE | SDF_GROUND; 870 s->n_chan = 8; 871 s->maxdata = (1 << thisboard->ai_nbits) - 1; 872 s->range_table = das08_ai_lranges[thisboard->ai_pg]; 873 s->insn_read = thisboard->ai; 874 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg]; 875 } else { 876 s->type = COMEDI_SUBD_UNUSED; 877 } 878 879 s = dev->subdevices + 1; 880 /* ao */ 881 if (thisboard->ao) { 882 s->type = COMEDI_SUBD_AO; 883/* XXX lacks read-back insn */ 884 s->subdev_flags = SDF_WRITABLE; 885 s->n_chan = 2; 886 s->maxdata = (1 << thisboard->ao_nbits) - 1; 887 s->range_table = &range_bipolar5; 888 s->insn_write = thisboard->ao; 889 } else { 890 s->type = COMEDI_SUBD_UNUSED; 891 } 892 893 s = dev->subdevices + 2; 894 /* di */ 895 if (thisboard->di) { 896 s->type = COMEDI_SUBD_DI; 897 s->subdev_flags = SDF_READABLE; 898 s->n_chan = (thisboard->di == das08_di_rbits) ? 3 : 8; 899 s->maxdata = 1; 900 s->range_table = &range_digital; 901 s->insn_bits = thisboard->di; 902 } else { 903 s->type = COMEDI_SUBD_UNUSED; 904 } 905 906 s = dev->subdevices + 3; 907 /* do */ 908 if (thisboard->do_) { 909 s->type = COMEDI_SUBD_DO; 910 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 911 s->n_chan = thisboard->do_nchan; 912 s->maxdata = 1; 913 s->range_table = &range_digital; 914 s->insn_bits = thisboard->do_; 915 } else { 916 s->type = COMEDI_SUBD_UNUSED; 917 } 918 919 s = dev->subdevices + 4; 920 /* 8255 */ 921 if (thisboard->i8255_offset != 0) { 922 subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase + 923 thisboard->i8255_offset)); 924 } else { 925 s->type = COMEDI_SUBD_UNUSED; 926 } 927 928 s = dev->subdevices + 5; 929 /* 8254 */ 930 if (thisboard->i8254_offset != 0) { 931 s->type = COMEDI_SUBD_COUNTER; 932 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 933 s->n_chan = 3; 934 s->maxdata = 0xFFFF; 935 s->insn_read = das08_counter_read; 936 s->insn_write = das08_counter_write; 937 s->insn_config = das08_counter_config; 938 /* Set-up the 8254 structure */ 939 devpriv->i8254.channels = 3; 940 devpriv->i8254.logic2phys[0] = 0; 941 devpriv->i8254.logic2phys[1] = 1; 942 devpriv->i8254.logic2phys[2] = 2; 943 devpriv->i8254.iobase = iobase + thisboard->i8254_offset; 944 devpriv->i8254.mode[0] = 945 devpriv->i8254.mode[1] = 946 devpriv->i8254.mode[2] = I8254_MODE0 | I8254_BINARY; 947 i8254_initialize(&devpriv->i8254); 948 } else { 949 s->type = COMEDI_SUBD_UNUSED; 950 } 951 952 return 0; 953} 954 955static int das08_attach(struct comedi_device *dev, struct comedi_devconfig *it) 956{ 957 int ret; 958 unsigned long iobase; 959#ifdef CONFIG_COMEDI_PCI 960 unsigned long pci_iobase = 0; 961 struct pci_dev *pdev; 962#endif 963 964 if ((ret = alloc_private(dev, sizeof(struct das08_private_struct))) < 0) 965 return ret; 966 967 printk("comedi%d: das08: ", dev->minor); 968 /* deal with a pci board */ 969 if (thisboard->bustype == pci) { 970#ifdef CONFIG_COMEDI_PCI 971 if (it->options[0] || it->options[1]) { 972 printk("bus %i slot %i ", 973 it->options[0], it->options[1]); 974 } 975 printk("\n"); 976 /* find card */ 977 for (pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); 978 pdev != NULL; 979 pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) { 980 if (pdev->vendor == PCI_VENDOR_ID_COMPUTERBOARDS 981 && pdev->device == PCI_DEVICE_ID_PCIDAS08) { 982 if (it->options[0] || it->options[1]) { 983 if (pdev->bus->number == it->options[0] 984 && PCI_SLOT(pdev->devfn) == 985 it->options[1]) { 986 break; 987 } 988 } else { 989 break; 990 } 991 } 992 } 993 if (!pdev) { 994 printk("No pci das08 cards found\n"); 995 return -EIO; 996 } 997 devpriv->pdev = pdev; 998 /* enable PCI device and reserve I/O spaces */ 999 if (comedi_pci_enable(pdev, DRV_NAME)) { 1000 printk(" Error enabling PCI device and requesting regions\n"); 1001 return -EIO; 1002 } 1003 /* read base addresses */ 1004 pci_iobase = pci_resource_start(pdev, 1); 1005 iobase = pci_resource_start(pdev, 2); 1006 printk("pcibase 0x%lx iobase 0x%lx\n", pci_iobase, iobase); 1007 devpriv->pci_iobase = pci_iobase; 1008#if 0 1009/* We could enable to pci-das08's interrupt here to make it possible 1010 * to do timed input in this driver, but there is little point since 1011 * conversions would have to be started by the interrupt handler 1012 * so you might as well use comedi_rt_timer to emulate commands 1013 */ 1014 /* set source of interrupt trigger to counter2 output */ 1015 outb(CNTRL_INTR | CNTRL_DIR, pci_iobase + CNTRL); 1016 /* Enable local interrupt 1 and pci interrupt */ 1017 outw(INTR1_ENABLE | PCI_INTR_ENABLE, pci_iobase + INTCSR); 1018#endif 1019#else /* CONFIG_COMEDI_PCI */ 1020 printk("this driver has not been built with PCI support.\n"); 1021 return -EINVAL; 1022#endif /* CONFIG_COMEDI_PCI */ 1023 } else { 1024 iobase = it->options[0]; 1025 } 1026 printk("\n"); 1027 1028 return das08_common_attach(dev, iobase); 1029} 1030 1031int das08_common_detach(struct comedi_device *dev) 1032{ 1033 printk(KERN_INFO "comedi%d: das08: remove\n", dev->minor); 1034 1035 if (dev->subdevices) 1036 subdev_8255_cleanup(dev, dev->subdevices + 4); 1037 1038 /* deallocate ioports for non-pcmcia, non-pci boards */ 1039 if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) { 1040 if (dev->iobase) 1041 release_region(dev->iobase, thisboard->iosize); 1042 } 1043 1044#ifdef CONFIG_COMEDI_PCI 1045 if (devpriv) { 1046 if (devpriv->pdev) { 1047 if (devpriv->pci_iobase) { 1048 comedi_pci_disable(devpriv->pdev); 1049 } 1050 pci_dev_put(devpriv->pdev); 1051 } 1052 } 1053#endif 1054 1055 return 0; 1056} 1057 1058#ifdef CONFIG_COMEDI_PCI 1059COMEDI_PCI_INITCLEANUP(driver_das08, das08_pci_table); 1060#else 1061COMEDI_INITCLEANUP(driver_das08); 1062#endif 1063 1064EXPORT_SYMBOL_GPL(das08_common_attach); 1065EXPORT_SYMBOL_GPL(das08_common_detach); 1066#ifdef CONFIG_COMEDI_PCMCIA 1067EXPORT_SYMBOL_GPL(das08_cs_boards); 1068#endif 1069