1/* 2 comedi/drivers/dmm32at.c 3 Diamond Systems mm32at code for a Comedi driver 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 2000 David A. Schleef <ds@schleef.org> 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17*/ 18/* 19Driver: dmm32at 20Description: Diamond Systems mm32at driver. 21Devices: 22Author: Perry J. Piplani <perry.j.piplani@nasa.gov> 23Updated: Fri Jun 4 09:13:24 CDT 2004 24Status: experimental 25 26This driver is for the Diamond Systems MM-32-AT board 27http://www.diamondsystems.com/products/diamondmm32at It is being used 28on serveral projects inside NASA, without problems so far. For analog 29input commands, TRIG_EXT is not yet supported at all.. 30 31Configuration Options: 32 comedi_config /dev/comedi0 dmm32at baseaddr,irq 33*/ 34 35#include <linux/module.h> 36#include <linux/delay.h> 37#include <linux/interrupt.h> 38#include "../comedidev.h" 39 40#include "comedi_fc.h" 41 42/* Board register addresses */ 43#define DMM32AT_CONV 0x00 44#define DMM32AT_AILSB 0x00 45#define DMM32AT_AUXDOUT 0x01 46#define DMM32AT_AIMSB 0x01 47#define DMM32AT_AILOW 0x02 48#define DMM32AT_AIHIGH 0x03 49 50#define DMM32AT_DACSTAT 0x04 51#define DMM32AT_DACLSB_REG 0x04 52#define DMM32AT_DACMSB_REG 0x05 53#define DMM32AT_DACMSB_CHAN(x) ((x) << 6) 54 55#define DMM32AT_FIFOCNTRL 0x07 56#define DMM32AT_FIFOSTAT 0x07 57 58#define DMM32AT_CNTRL 0x08 59#define DMM32AT_AISTAT 0x08 60 61#define DMM32AT_INTCLOCK 0x09 62 63#define DMM32AT_CNTRDIO 0x0a 64 65#define DMM32AT_AICONF 0x0b 66#define DMM32AT_AIRBACK 0x0b 67 68#define DMM32AT_CLK1 0x0d 69#define DMM32AT_CLK2 0x0e 70#define DMM32AT_CLKCT 0x0f 71 72#define DMM32AT_DIOA 0x0c 73#define DMM32AT_DIOB 0x0d 74#define DMM32AT_DIOC 0x0e 75#define DMM32AT_DIOCONF 0x0f 76 77/* Board register values. */ 78 79/* DMM32AT_DACSTAT 0x04 */ 80#define DMM32AT_DACBUSY 0x80 81 82/* DMM32AT_FIFOCNTRL 0x07 */ 83#define DMM32AT_FIFORESET 0x02 84#define DMM32AT_SCANENABLE 0x04 85 86/* DMM32AT_CNTRL 0x08 */ 87#define DMM32AT_RESET 0x20 88#define DMM32AT_INTRESET 0x08 89#define DMM32AT_CLKACC 0x00 90#define DMM32AT_DIOACC 0x01 91 92/* DMM32AT_AISTAT 0x08 */ 93#define DMM32AT_STATUS 0x80 94 95/* DMM32AT_INTCLOCK 0x09 */ 96#define DMM32AT_ADINT 0x80 97#define DMM32AT_CLKSEL 0x03 98 99/* DMM32AT_CNTRDIO 0x0a */ 100#define DMM32AT_FREQ12 0x80 101 102/* DMM32AT_AICONF 0x0b */ 103#define DMM32AT_RANGE_U10 0x0c 104#define DMM32AT_RANGE_U5 0x0d 105#define DMM32AT_RANGE_B10 0x08 106#define DMM32AT_RANGE_B5 0x00 107#define DMM32AT_SCINT_20 0x00 108#define DMM32AT_SCINT_15 0x10 109#define DMM32AT_SCINT_10 0x20 110#define DMM32AT_SCINT_5 0x30 111 112/* DMM32AT_CLKCT 0x0f */ 113#define DMM32AT_CLKCT1 0x56 /* mode3 counter 1 - write low byte only */ 114#define DMM32AT_CLKCT2 0xb6 /* mode3 counter 2 - write high and low byte */ 115 116/* DMM32AT_DIOCONF 0x0f */ 117#define DMM32AT_DIENABLE 0x80 118#define DMM32AT_DIRA 0x10 119#define DMM32AT_DIRB 0x02 120#define DMM32AT_DIRCL 0x01 121#define DMM32AT_DIRCH 0x08 122 123/* board AI ranges in comedi structure */ 124static const struct comedi_lrange dmm32at_airanges = { 125 4, { 126 UNI_RANGE(10), 127 UNI_RANGE(5), 128 BIP_RANGE(10), 129 BIP_RANGE(5) 130 } 131}; 132 133/* register values for above ranges */ 134static const unsigned char dmm32at_rangebits[] = { 135 DMM32AT_RANGE_U10, 136 DMM32AT_RANGE_U5, 137 DMM32AT_RANGE_B10, 138 DMM32AT_RANGE_B5, 139}; 140 141/* only one of these ranges is valid, as set by a jumper on the 142 * board. The application should only use the range set by the jumper 143 */ 144static const struct comedi_lrange dmm32at_aoranges = { 145 4, { 146 UNI_RANGE(10), 147 UNI_RANGE(5), 148 BIP_RANGE(10), 149 BIP_RANGE(5) 150 } 151}; 152 153struct dmm32at_private { 154 int data; 155 int ai_inuse; 156 unsigned int ai_scans_left; 157 unsigned char dio_config; 158}; 159 160static int dmm32at_ai_status(struct comedi_device *dev, 161 struct comedi_subdevice *s, 162 struct comedi_insn *insn, 163 unsigned long context) 164{ 165 unsigned char status; 166 167 status = inb(dev->iobase + context); 168 if ((status & DMM32AT_STATUS) == 0) 169 return 0; 170 return -EBUSY; 171} 172 173static int dmm32at_ai_rinsn(struct comedi_device *dev, 174 struct comedi_subdevice *s, 175 struct comedi_insn *insn, unsigned int *data) 176{ 177 int n; 178 unsigned int d; 179 unsigned short msb, lsb; 180 unsigned char chan; 181 int range; 182 int ret; 183 184 /* get the channel and range number */ 185 186 chan = CR_CHAN(insn->chanspec) & (s->n_chan - 1); 187 range = CR_RANGE(insn->chanspec); 188 189 /* zero scan and fifo control and reset fifo */ 190 outb(DMM32AT_FIFORESET, dev->iobase + DMM32AT_FIFOCNTRL); 191 192 /* write the ai channel range regs */ 193 outb(chan, dev->iobase + DMM32AT_AILOW); 194 outb(chan, dev->iobase + DMM32AT_AIHIGH); 195 /* set the range bits */ 196 outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AICONF); 197 198 /* wait for circuit to settle */ 199 ret = comedi_timeout(dev, s, insn, dmm32at_ai_status, DMM32AT_AIRBACK); 200 if (ret) 201 return ret; 202 203 /* convert n samples */ 204 for (n = 0; n < insn->n; n++) { 205 /* trigger conversion */ 206 outb(0xff, dev->iobase + DMM32AT_CONV); 207 208 /* wait for conversion to end */ 209 ret = comedi_timeout(dev, s, insn, dmm32at_ai_status, 210 DMM32AT_AISTAT); 211 if (ret) 212 return ret; 213 214 /* read data */ 215 lsb = inb(dev->iobase + DMM32AT_AILSB); 216 msb = inb(dev->iobase + DMM32AT_AIMSB); 217 218 /* invert sign bit to make range unsigned, this is an 219 idiosyncrasy of the diamond board, it return 220 conversions as a signed value, i.e. -32768 to 221 32767, flipping the bit and interpreting it as 222 signed gives you a range of 0 to 65535 which is 223 used by comedi */ 224 d = ((msb ^ 0x0080) << 8) + lsb; 225 226 data[n] = d; 227 } 228 229 /* return the number of samples read/written */ 230 return n; 231} 232 233static int dmm32at_ns_to_timer(unsigned int *ns, unsigned int flags) 234{ 235 /* trivial timer */ 236 return *ns; 237} 238 239static int dmm32at_ai_check_chanlist(struct comedi_device *dev, 240 struct comedi_subdevice *s, 241 struct comedi_cmd *cmd) 242{ 243 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]); 244 unsigned int range0 = CR_RANGE(cmd->chanlist[0]); 245 int i; 246 247 for (i = 1; i < cmd->chanlist_len; i++) { 248 unsigned int chan = CR_CHAN(cmd->chanlist[i]); 249 unsigned int range = CR_RANGE(cmd->chanlist[i]); 250 251 if (chan != (chan0 + i) % s->n_chan) { 252 dev_dbg(dev->class_dev, 253 "entries in chanlist must be consecutive channels, counting upwards\n"); 254 return -EINVAL; 255 } 256 if (range != range0) { 257 dev_dbg(dev->class_dev, 258 "entries in chanlist must all have the same gain\n"); 259 return -EINVAL; 260 } 261 } 262 263 return 0; 264} 265 266static int dmm32at_ai_cmdtest(struct comedi_device *dev, 267 struct comedi_subdevice *s, 268 struct comedi_cmd *cmd) 269{ 270 int err = 0; 271 unsigned int arg; 272 273 /* Step 1 : check if triggers are trivially valid */ 274 275 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW); 276 err |= cfc_check_trigger_src(&cmd->scan_begin_src, 277 TRIG_TIMER /*| TRIG_EXT */); 278 err |= cfc_check_trigger_src(&cmd->convert_src, 279 TRIG_TIMER /*| TRIG_EXT */); 280 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 281 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 282 283 if (err) 284 return 1; 285 286 /* Step 2a : make sure trigger sources are unique */ 287 288 err |= cfc_check_trigger_is_unique(cmd->scan_begin_src); 289 err |= cfc_check_trigger_is_unique(cmd->convert_src); 290 err |= cfc_check_trigger_is_unique(cmd->stop_src); 291 292 /* Step 2b : and mutually compatible */ 293 294 if (err) 295 return 2; 296 297 /* Step 3: check if arguments are trivially valid */ 298 299 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); 300 301#define MAX_SCAN_SPEED 1000000 /* in nanoseconds */ 302#define MIN_SCAN_SPEED 1000000000 /* in nanoseconds */ 303 304 if (cmd->scan_begin_src == TRIG_TIMER) { 305 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, 306 MAX_SCAN_SPEED); 307 err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg, 308 MIN_SCAN_SPEED); 309 } else { 310 /* external trigger */ 311 /* should be level/edge, hi/lo specification here */ 312 /* should specify multiple external triggers */ 313 err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg, 9); 314 } 315 316 if (cmd->convert_src == TRIG_TIMER) { 317 if (cmd->convert_arg >= 17500) 318 cmd->convert_arg = 20000; 319 else if (cmd->convert_arg >= 12500) 320 cmd->convert_arg = 15000; 321 else if (cmd->convert_arg >= 7500) 322 cmd->convert_arg = 10000; 323 else 324 cmd->convert_arg = 5000; 325 } else { 326 /* external trigger */ 327 /* see above */ 328 err |= cfc_check_trigger_arg_max(&cmd->convert_arg, 9); 329 } 330 331 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); 332 333 if (cmd->stop_src == TRIG_COUNT) { 334 err |= cfc_check_trigger_arg_max(&cmd->stop_arg, 0xfffffff0); 335 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1); 336 } else { 337 /* TRIG_NONE */ 338 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); 339 } 340 341 if (err) 342 return 3; 343 344 /* step 4: fix up any arguments */ 345 346 if (cmd->scan_begin_src == TRIG_TIMER) { 347 arg = cmd->scan_begin_arg; 348 dmm32at_ns_to_timer(&arg, cmd->flags); 349 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg); 350 } 351 if (cmd->convert_src == TRIG_TIMER) { 352 arg = cmd->convert_arg; 353 dmm32at_ns_to_timer(&arg, cmd->flags); 354 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg); 355 356 if (cmd->scan_begin_src == TRIG_TIMER) { 357 arg = cmd->convert_arg * cmd->scan_end_arg; 358 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, 359 arg); 360 } 361 } 362 363 if (err) 364 return 4; 365 366 /* Step 5: check channel list if it exists */ 367 if (cmd->chanlist && cmd->chanlist_len > 0) 368 err |= dmm32at_ai_check_chanlist(dev, s, cmd); 369 370 if (err) 371 return 5; 372 373 return 0; 374} 375 376static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec) 377{ 378 unsigned char lo1, lo2, hi2; 379 unsigned short both2; 380 381 /* based on 10mhz clock */ 382 lo1 = 200; 383 both2 = nansec / 20000; 384 hi2 = (both2 & 0xff00) >> 8; 385 lo2 = both2 & 0x00ff; 386 387 /* set the counter frequency to 10mhz */ 388 outb(0, dev->iobase + DMM32AT_CNTRDIO); 389 390 /* get access to the clock regs */ 391 outb(DMM32AT_CLKACC, dev->iobase + DMM32AT_CNTRL); 392 393 /* write the counter 1 control word and low byte to counter */ 394 outb(DMM32AT_CLKCT1, dev->iobase + DMM32AT_CLKCT); 395 outb(lo1, dev->iobase + DMM32AT_CLK1); 396 397 /* write the counter 2 control word and low byte then to counter */ 398 outb(DMM32AT_CLKCT2, dev->iobase + DMM32AT_CLKCT); 399 outb(lo2, dev->iobase + DMM32AT_CLK2); 400 outb(hi2, dev->iobase + DMM32AT_CLK2); 401 402 /* enable the ai conversion interrupt and the clock to start scans */ 403 outb(DMM32AT_ADINT | DMM32AT_CLKSEL, dev->iobase + DMM32AT_INTCLOCK); 404} 405 406static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 407{ 408 struct dmm32at_private *devpriv = dev->private; 409 struct comedi_cmd *cmd = &s->async->cmd; 410 int range; 411 unsigned char chanlo, chanhi; 412 int ret; 413 414 if (!cmd->chanlist) 415 return -EINVAL; 416 417 /* get the channel list and range */ 418 chanlo = CR_CHAN(cmd->chanlist[0]) & (s->n_chan - 1); 419 chanhi = chanlo + cmd->chanlist_len - 1; 420 if (chanhi >= s->n_chan) 421 return -EINVAL; 422 range = CR_RANGE(cmd->chanlist[0]); 423 424 /* reset fifo */ 425 outb(DMM32AT_FIFORESET, dev->iobase + DMM32AT_FIFOCNTRL); 426 427 /* set scan enable */ 428 outb(DMM32AT_SCANENABLE, dev->iobase + DMM32AT_FIFOCNTRL); 429 430 /* write the ai channel range regs */ 431 outb(chanlo, dev->iobase + DMM32AT_AILOW); 432 outb(chanhi, dev->iobase + DMM32AT_AIHIGH); 433 434 /* set the range bits */ 435 outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AICONF); 436 437 /* reset the interrupt just in case */ 438 outb(DMM32AT_INTRESET, dev->iobase + DMM32AT_CNTRL); 439 440 if (cmd->stop_src == TRIG_COUNT) 441 devpriv->ai_scans_left = cmd->stop_arg; 442 else { /* TRIG_NONE */ 443 devpriv->ai_scans_left = 0xffffffff; /* indicates TRIG_NONE to 444 * isr */ 445 } 446 447 /* 448 * wait for circuit to settle 449 * we don't have the 'insn' here but it's not needed 450 */ 451 ret = comedi_timeout(dev, s, NULL, dmm32at_ai_status, DMM32AT_AIRBACK); 452 if (ret) 453 return ret; 454 455 if (devpriv->ai_scans_left > 1) { 456 /* start the clock and enable the interrupts */ 457 dmm32at_setaitimer(dev, cmd->scan_begin_arg); 458 } else { 459 /* start the interrups and initiate a single scan */ 460 outb(DMM32AT_ADINT, dev->iobase + DMM32AT_INTCLOCK); 461 outb(0xff, dev->iobase + DMM32AT_CONV); 462 } 463 464 return 0; 465 466} 467 468static int dmm32at_ai_cancel(struct comedi_device *dev, 469 struct comedi_subdevice *s) 470{ 471 struct dmm32at_private *devpriv = dev->private; 472 473 devpriv->ai_scans_left = 1; 474 return 0; 475} 476 477static irqreturn_t dmm32at_isr(int irq, void *d) 478{ 479 struct comedi_device *dev = d; 480 struct dmm32at_private *devpriv = dev->private; 481 unsigned char intstat; 482 unsigned int samp; 483 unsigned short msb, lsb; 484 int i; 485 486 if (!dev->attached) { 487 dev_err(dev->class_dev, "spurious interrupt\n"); 488 return IRQ_HANDLED; 489 } 490 491 intstat = inb(dev->iobase + DMM32AT_INTCLOCK); 492 493 if (intstat & DMM32AT_ADINT) { 494 struct comedi_subdevice *s = dev->read_subdev; 495 struct comedi_cmd *cmd = &s->async->cmd; 496 497 for (i = 0; i < cmd->chanlist_len; i++) { 498 /* read data */ 499 lsb = inb(dev->iobase + DMM32AT_AILSB); 500 msb = inb(dev->iobase + DMM32AT_AIMSB); 501 502 /* invert sign bit to make range unsigned */ 503 samp = ((msb ^ 0x0080) << 8) + lsb; 504 comedi_buf_put(s, samp); 505 } 506 507 if (devpriv->ai_scans_left != 0xffffffff) { /* TRIG_COUNT */ 508 devpriv->ai_scans_left--; 509 if (devpriv->ai_scans_left == 0) { 510 /* disable further interrupts and clocks */ 511 outb(0x0, dev->iobase + DMM32AT_INTCLOCK); 512 /* set the buffer to be flushed with an EOF */ 513 s->async->events |= COMEDI_CB_EOA; 514 } 515 516 } 517 /* flush the buffer */ 518 comedi_event(dev, s); 519 } 520 521 /* reset the interrupt */ 522 outb(DMM32AT_INTRESET, dev->iobase + DMM32AT_CNTRL); 523 return IRQ_HANDLED; 524} 525 526static int dmm32at_ao_eoc(struct comedi_device *dev, 527 struct comedi_subdevice *s, 528 struct comedi_insn *insn, 529 unsigned long context) 530{ 531 unsigned char status; 532 533 status = inb(dev->iobase + DMM32AT_DACSTAT); 534 if ((status & DMM32AT_DACBUSY) == 0) 535 return 0; 536 return -EBUSY; 537} 538 539static int dmm32at_ao_insn_write(struct comedi_device *dev, 540 struct comedi_subdevice *s, 541 struct comedi_insn *insn, 542 unsigned int *data) 543{ 544 unsigned int chan = CR_CHAN(insn->chanspec); 545 int i; 546 547 for (i = 0; i < insn->n; i++) { 548 unsigned int val = data[i]; 549 int ret; 550 551 /* write LSB then MSB + chan to load DAC */ 552 outb(val & 0xff, dev->iobase + DMM32AT_DACLSB_REG); 553 outb((val >> 8) | DMM32AT_DACMSB_CHAN(chan), 554 dev->iobase + DMM32AT_DACMSB_REG); 555 556 /* wait for circuit to settle */ 557 ret = comedi_timeout(dev, s, insn, dmm32at_ao_eoc, 0); 558 if (ret) 559 return ret; 560 561 /* dummy read to update DAC */ 562 inb(dev->iobase + DMM32AT_DACMSB_REG); 563 564 s->readback[chan] = val; 565 } 566 567 return insn->n; 568} 569 570static int dmm32at_dio_insn_bits(struct comedi_device *dev, 571 struct comedi_subdevice *s, 572 struct comedi_insn *insn, 573 unsigned int *data) 574{ 575 struct dmm32at_private *devpriv = dev->private; 576 unsigned int mask; 577 unsigned int val; 578 579 mask = comedi_dio_update_state(s, data); 580 if (mask) { 581 /* get access to the DIO regs */ 582 outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL); 583 584 /* if either part of dio is set for output */ 585 if (((devpriv->dio_config & DMM32AT_DIRCL) == 0) || 586 ((devpriv->dio_config & DMM32AT_DIRCH) == 0)) { 587 val = (s->state & 0x00ff0000) >> 16; 588 outb(val, dev->iobase + DMM32AT_DIOC); 589 } 590 if ((devpriv->dio_config & DMM32AT_DIRB) == 0) { 591 val = (s->state & 0x0000ff00) >> 8; 592 outb(val, dev->iobase + DMM32AT_DIOB); 593 } 594 if ((devpriv->dio_config & DMM32AT_DIRA) == 0) { 595 val = (s->state & 0x000000ff); 596 outb(val, dev->iobase + DMM32AT_DIOA); 597 } 598 } 599 600 val = inb(dev->iobase + DMM32AT_DIOA); 601 val |= inb(dev->iobase + DMM32AT_DIOB) << 8; 602 val |= inb(dev->iobase + DMM32AT_DIOC) << 16; 603 s->state = val; 604 605 data[1] = val; 606 607 return insn->n; 608} 609 610static int dmm32at_dio_insn_config(struct comedi_device *dev, 611 struct comedi_subdevice *s, 612 struct comedi_insn *insn, 613 unsigned int *data) 614{ 615 struct dmm32at_private *devpriv = dev->private; 616 unsigned int chan = CR_CHAN(insn->chanspec); 617 unsigned int mask; 618 unsigned char chanbit; 619 int ret; 620 621 if (chan < 8) { 622 mask = 0x0000ff; 623 chanbit = DMM32AT_DIRA; 624 } else if (chan < 16) { 625 mask = 0x00ff00; 626 chanbit = DMM32AT_DIRB; 627 } else if (chan < 20) { 628 mask = 0x0f0000; 629 chanbit = DMM32AT_DIRCL; 630 } else { 631 mask = 0xf00000; 632 chanbit = DMM32AT_DIRCH; 633 } 634 635 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 636 if (ret) 637 return ret; 638 639 if (data[0] == INSN_CONFIG_DIO_OUTPUT) 640 devpriv->dio_config &= ~chanbit; 641 else 642 devpriv->dio_config |= chanbit; 643 /* get access to the DIO regs */ 644 outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL); 645 /* set the DIO's to the new configuration setting */ 646 outb(devpriv->dio_config, dev->iobase + DMM32AT_DIOCONF); 647 648 return insn->n; 649} 650 651static int dmm32at_attach(struct comedi_device *dev, 652 struct comedi_devconfig *it) 653{ 654 struct dmm32at_private *devpriv; 655 int ret; 656 struct comedi_subdevice *s; 657 unsigned char aihi, ailo, fifostat, aistat, intstat, airback; 658 659 ret = comedi_request_region(dev, it->options[0], 0x10); 660 if (ret) 661 return ret; 662 663 /* the following just makes sure the board is there and gets 664 it to a known state */ 665 666 /* reset the board */ 667 outb(DMM32AT_RESET, dev->iobase + DMM32AT_CNTRL); 668 669 /* allow a millisecond to reset */ 670 udelay(1000); 671 672 /* zero scan and fifo control */ 673 outb(0x0, dev->iobase + DMM32AT_FIFOCNTRL); 674 675 /* zero interrupt and clock control */ 676 outb(0x0, dev->iobase + DMM32AT_INTCLOCK); 677 678 /* write a test channel range, the high 3 bits should drop */ 679 outb(0x80, dev->iobase + DMM32AT_AILOW); 680 outb(0xff, dev->iobase + DMM32AT_AIHIGH); 681 682 /* set the range at 10v unipolar */ 683 outb(DMM32AT_RANGE_U10, dev->iobase + DMM32AT_AICONF); 684 685 /* should take 10 us to settle, here's a hundred */ 686 udelay(100); 687 688 /* read back the values */ 689 ailo = inb(dev->iobase + DMM32AT_AILOW); 690 aihi = inb(dev->iobase + DMM32AT_AIHIGH); 691 fifostat = inb(dev->iobase + DMM32AT_FIFOSTAT); 692 aistat = inb(dev->iobase + DMM32AT_AISTAT); 693 intstat = inb(dev->iobase + DMM32AT_INTCLOCK); 694 airback = inb(dev->iobase + DMM32AT_AIRBACK); 695 696 if ((ailo != 0x00) || (aihi != 0x1f) || (fifostat != 0x80) || 697 (aistat != 0x60 || (intstat != 0x00) || airback != 0x0c)) { 698 dev_err(dev->class_dev, "board detection failed\n"); 699 return -EIO; 700 } 701 702 if (it->options[1]) { 703 ret = request_irq(it->options[1], dmm32at_isr, 0, 704 dev->board_name, dev); 705 if (ret == 0) 706 dev->irq = it->options[1]; 707 } 708 709 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 710 if (!devpriv) 711 return -ENOMEM; 712 713 ret = comedi_alloc_subdevices(dev, 3); 714 if (ret) 715 return ret; 716 717 s = &dev->subdevices[0]; 718 /* analog input subdevice */ 719 s->type = COMEDI_SUBD_AI; 720 /* we support single-ended (ground) and differential */ 721 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; 722 s->n_chan = 32; 723 s->maxdata = 0xffff; 724 s->range_table = &dmm32at_airanges; 725 s->insn_read = dmm32at_ai_rinsn; 726 if (dev->irq) { 727 dev->read_subdev = s; 728 s->subdev_flags |= SDF_CMD_READ; 729 s->len_chanlist = 32; 730 s->do_cmd = dmm32at_ai_cmd; 731 s->do_cmdtest = dmm32at_ai_cmdtest; 732 s->cancel = dmm32at_ai_cancel; 733 } 734 735 s = &dev->subdevices[1]; 736 /* analog output subdevice */ 737 s->type = COMEDI_SUBD_AO; 738 s->subdev_flags = SDF_WRITABLE; 739 s->n_chan = 4; 740 s->maxdata = 0x0fff; 741 s->range_table = &dmm32at_aoranges; 742 s->insn_write = dmm32at_ao_insn_write; 743 s->insn_read = comedi_readback_insn_read; 744 745 ret = comedi_alloc_subdev_readback(s); 746 if (ret) 747 return ret; 748 749 s = &dev->subdevices[2]; 750 /* digital i/o subdevice */ 751 752 /* get access to the DIO regs */ 753 outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL); 754 /* set the DIO's to the defualt input setting */ 755 devpriv->dio_config = DMM32AT_DIRA | DMM32AT_DIRB | 756 DMM32AT_DIRCL | DMM32AT_DIRCH | DMM32AT_DIENABLE; 757 outb(devpriv->dio_config, dev->iobase + DMM32AT_DIOCONF); 758 759 /* set up the subdevice */ 760 s->type = COMEDI_SUBD_DIO; 761 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 762 s->n_chan = 24; 763 s->maxdata = 1; 764 s->state = 0; 765 s->range_table = &range_digital; 766 s->insn_bits = dmm32at_dio_insn_bits; 767 s->insn_config = dmm32at_dio_insn_config; 768 769 return 0; 770} 771 772static struct comedi_driver dmm32at_driver = { 773 .driver_name = "dmm32at", 774 .module = THIS_MODULE, 775 .attach = dmm32at_attach, 776 .detach = comedi_legacy_detach, 777}; 778module_comedi_driver(dmm32at_driver); 779 780MODULE_AUTHOR("Comedi http://www.comedi.org"); 781MODULE_DESCRIPTION("Comedi low-level driver"); 782MODULE_LICENSE("GPL"); 783