1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * adl_pci9111.c 4 * Hardware driver for PCI9111 ADLink cards: PCI-9111HR 5 * Copyright (C) 2002-2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr> 6 */ 7 8 /* 9 * Driver: adl_pci9111 10 * Description: Adlink PCI-9111HR 11 * Devices: [ADLink] PCI-9111HR (adl_pci9111) 12 * Author: Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr> 13 * Status: experimental 14 * 15 * Configuration options: not applicable, uses PCI auto config 16 * 17 * Supports: 18 * - ai_insn read 19 * - ao_insn read/write 20 * - di_insn read 21 * - do_insn read/write 22 * - ai_do_cmd mode with the following sources: 23 * - start_src TRIG_NOW 24 * - scan_begin_src TRIG_FOLLOW TRIG_TIMER TRIG_EXT 25 * - convert_src TRIG_TIMER TRIG_EXT 26 * - scan_end_src TRIG_COUNT 27 * - stop_src TRIG_COUNT TRIG_NONE 28 * 29 * The scanned channels must be consecutive and start from 0. They must 30 * all have the same range and aref. 31 */ 32 33 /* 34 * TODO: 35 * - Really test implemented functionality. 36 * - Add support for the PCI-9111DG with a probe routine to identify 37 * the card type (perhaps with the help of the channel number readback 38 * of the A/D Data register). 39 * - Add external multiplexer support. 40 */ 41 42 #include <linux/module.h> 43 #include <linux/delay.h> 44 #include <linux/interrupt.h> 45 #include <linux/comedi/comedi_pci.h> 46 #include <linux/comedi/comedi_8254.h> 47 48 #include "plx9052.h" 49 50 #define PCI9111_FIFO_HALF_SIZE 512 51 52 #define PCI9111_AI_ACQUISITION_PERIOD_MIN_NS 10000 53 54 #define PCI9111_RANGE_SETTING_DELAY 10 55 #define PCI9111_AI_INSTANT_READ_UDELAY_US 2 56 57 /* 58 * IO address map and bit defines 59 */ 60 #define PCI9111_AI_FIFO_REG 0x00 61 #define PCI9111_AO_REG 0x00 62 #define PCI9111_DIO_REG 0x02 63 #define PCI9111_EDIO_REG 0x04 64 #define PCI9111_AI_CHANNEL_REG 0x06 65 #define PCI9111_AI_RANGE_STAT_REG 0x08 66 #define PCI9111_AI_STAT_AD_BUSY BIT(7) 67 #define PCI9111_AI_STAT_FF_FF BIT(6) 68 #define PCI9111_AI_STAT_FF_HF BIT(5) 69 #define PCI9111_AI_STAT_FF_EF BIT(4) 70 #define PCI9111_AI_RANGE(x) (((x) & 0x7) << 0) 71 #define PCI9111_AI_RANGE_MASK PCI9111_AI_RANGE(7) 72 #define PCI9111_AI_TRIG_CTRL_REG 0x0a 73 #define PCI9111_AI_TRIG_CTRL_TRGEVENT BIT(5) 74 #define PCI9111_AI_TRIG_CTRL_POTRG BIT(4) 75 #define PCI9111_AI_TRIG_CTRL_PTRG BIT(3) 76 #define PCI9111_AI_TRIG_CTRL_ETIS BIT(2) 77 #define PCI9111_AI_TRIG_CTRL_TPST BIT(1) 78 #define PCI9111_AI_TRIG_CTRL_ASCAN BIT(0) 79 #define PCI9111_INT_CTRL_REG 0x0c 80 #define PCI9111_INT_CTRL_ISC2 BIT(3) 81 #define PCI9111_INT_CTRL_FFEN BIT(2) 82 #define PCI9111_INT_CTRL_ISC1 BIT(1) 83 #define PCI9111_INT_CTRL_ISC0 BIT(0) 84 #define PCI9111_SOFT_TRIG_REG 0x0e 85 #define PCI9111_8254_BASE_REG 0x40 86 #define PCI9111_INT_CLR_REG 0x48 87 88 /* PLX 9052 Local Interrupt 1 enabled and active */ 89 #define PCI9111_LI1_ACTIVE (PLX9052_INTCSR_LI1ENAB | \ 90 PLX9052_INTCSR_LI1STAT) 91 92 /* PLX 9052 Local Interrupt 2 enabled and active */ 93 #define PCI9111_LI2_ACTIVE (PLX9052_INTCSR_LI2ENAB | \ 94 PLX9052_INTCSR_LI2STAT) 95 96 static const struct comedi_lrange pci9111_ai_range = { 97 5, { 98 BIP_RANGE(10), 99 BIP_RANGE(5), 100 BIP_RANGE(2.5), 101 BIP_RANGE(1.25), 102 BIP_RANGE(0.625) 103 } 104 }; 105 106 struct pci9111_private_data { 107 unsigned long lcr_io_base; 108 109 unsigned int scan_delay; 110 unsigned int chunk_counter; 111 unsigned int chunk_num_samples; 112 113 unsigned short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE]; 114 }; 115 plx9050_interrupt_control(unsigned long io_base,bool int1_enable,bool int1_active_high,bool int2_enable,bool int2_active_high,bool interrupt_enable)116 static void plx9050_interrupt_control(unsigned long io_base, 117 bool int1_enable, 118 bool int1_active_high, 119 bool int2_enable, 120 bool int2_active_high, 121 bool interrupt_enable) 122 { 123 int flags = 0; 124 125 if (int1_enable) 126 flags |= PLX9052_INTCSR_LI1ENAB; 127 if (int1_active_high) 128 flags |= PLX9052_INTCSR_LI1POL; 129 if (int2_enable) 130 flags |= PLX9052_INTCSR_LI2ENAB; 131 if (int2_active_high) 132 flags |= PLX9052_INTCSR_LI2POL; 133 134 if (interrupt_enable) 135 flags |= PLX9052_INTCSR_PCIENAB; 136 137 outb(flags, io_base + PLX9052_INTCSR); 138 } 139 140 enum pci9111_ISC0_sources { 141 irq_on_eoc, 142 irq_on_fifo_half_full 143 }; 144 145 enum pci9111_ISC1_sources { 146 irq_on_timer_tick, 147 irq_on_external_trigger 148 }; 149 pci9111_interrupt_source_set(struct comedi_device * dev,enum pci9111_ISC0_sources irq_0_source,enum pci9111_ISC1_sources irq_1_source)150 static void pci9111_interrupt_source_set(struct comedi_device *dev, 151 enum pci9111_ISC0_sources irq_0_source, 152 enum pci9111_ISC1_sources irq_1_source) 153 { 154 int flags; 155 156 /* Read the current interrupt control bits */ 157 flags = inb(dev->iobase + PCI9111_AI_TRIG_CTRL_REG); 158 /* Shift the bits so they are compatible with the write register */ 159 flags >>= 4; 160 /* Mask off the ISCx bits */ 161 flags &= 0xc0; 162 163 /* Now set the new ISCx bits */ 164 if (irq_0_source == irq_on_fifo_half_full) 165 flags |= PCI9111_INT_CTRL_ISC0; 166 167 if (irq_1_source == irq_on_external_trigger) 168 flags |= PCI9111_INT_CTRL_ISC1; 169 170 outb(flags, dev->iobase + PCI9111_INT_CTRL_REG); 171 } 172 pci9111_fifo_reset(struct comedi_device * dev)173 static void pci9111_fifo_reset(struct comedi_device *dev) 174 { 175 unsigned long int_ctrl_reg = dev->iobase + PCI9111_INT_CTRL_REG; 176 177 /* To reset the FIFO, set FFEN sequence as 0 -> 1 -> 0 */ 178 outb(0, int_ctrl_reg); 179 outb(PCI9111_INT_CTRL_FFEN, int_ctrl_reg); 180 outb(0, int_ctrl_reg); 181 } 182 pci9111_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)183 static int pci9111_ai_cancel(struct comedi_device *dev, 184 struct comedi_subdevice *s) 185 { 186 struct pci9111_private_data *dev_private = dev->private; 187 188 /* Disable interrupts */ 189 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true, 190 true, false); 191 192 /* disable A/D triggers (software trigger mode) and auto scan off */ 193 outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG); 194 195 pci9111_fifo_reset(dev); 196 197 return 0; 198 } 199 pci9111_ai_check_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)200 static int pci9111_ai_check_chanlist(struct comedi_device *dev, 201 struct comedi_subdevice *s, 202 struct comedi_cmd *cmd) 203 { 204 unsigned int range0 = CR_RANGE(cmd->chanlist[0]); 205 unsigned int aref0 = CR_AREF(cmd->chanlist[0]); 206 int i; 207 208 for (i = 1; i < cmd->chanlist_len; i++) { 209 unsigned int chan = CR_CHAN(cmd->chanlist[i]); 210 unsigned int range = CR_RANGE(cmd->chanlist[i]); 211 unsigned int aref = CR_AREF(cmd->chanlist[i]); 212 213 if (chan != i) { 214 dev_dbg(dev->class_dev, 215 "entries in chanlist must be consecutive channels,counting upwards from 0\n"); 216 return -EINVAL; 217 } 218 219 if (range != range0) { 220 dev_dbg(dev->class_dev, 221 "entries in chanlist must all have the same gain\n"); 222 return -EINVAL; 223 } 224 225 if (aref != aref0) { 226 dev_dbg(dev->class_dev, 227 "entries in chanlist must all have the same reference\n"); 228 return -EINVAL; 229 } 230 } 231 232 return 0; 233 } 234 pci9111_ai_do_cmd_test(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)235 static int pci9111_ai_do_cmd_test(struct comedi_device *dev, 236 struct comedi_subdevice *s, 237 struct comedi_cmd *cmd) 238 { 239 int err = 0; 240 unsigned int arg; 241 242 /* Step 1 : check if triggers are trivially valid */ 243 244 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); 245 err |= comedi_check_trigger_src(&cmd->scan_begin_src, 246 TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT); 247 err |= comedi_check_trigger_src(&cmd->convert_src, 248 TRIG_TIMER | TRIG_EXT); 249 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 250 err |= comedi_check_trigger_src(&cmd->stop_src, 251 TRIG_COUNT | TRIG_NONE); 252 253 if (err) 254 return 1; 255 256 /* Step 2a : make sure trigger sources are unique */ 257 258 err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); 259 err |= comedi_check_trigger_is_unique(cmd->convert_src); 260 err |= comedi_check_trigger_is_unique(cmd->stop_src); 261 262 /* Step 2b : and mutually compatible */ 263 264 if (cmd->scan_begin_src != TRIG_FOLLOW) { 265 if (cmd->scan_begin_src != cmd->convert_src) 266 err |= -EINVAL; 267 } 268 269 if (err) 270 return 2; 271 272 /* Step 3: check if arguments are trivially valid */ 273 274 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); 275 276 if (cmd->convert_src == TRIG_TIMER) { 277 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 278 PCI9111_AI_ACQUISITION_PERIOD_MIN_NS); 279 } else { /* TRIG_EXT */ 280 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); 281 } 282 283 if (cmd->scan_begin_src == TRIG_TIMER) { 284 err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, 285 PCI9111_AI_ACQUISITION_PERIOD_MIN_NS); 286 } else { /* TRIG_FOLLOW || TRIG_EXT */ 287 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 288 } 289 290 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, 291 cmd->chanlist_len); 292 293 if (cmd->stop_src == TRIG_COUNT) 294 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); 295 else /* TRIG_NONE */ 296 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); 297 298 if (err) 299 return 3; 300 301 /* Step 4: fix up any arguments */ 302 303 if (cmd->convert_src == TRIG_TIMER) { 304 arg = cmd->convert_arg; 305 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); 306 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); 307 } 308 309 /* 310 * There's only one timer on this card, so the scan_begin timer 311 * must be a multiple of chanlist_len*convert_arg 312 */ 313 if (cmd->scan_begin_src == TRIG_TIMER) { 314 arg = cmd->chanlist_len * cmd->convert_arg; 315 316 if (arg < cmd->scan_begin_arg) 317 arg *= (cmd->scan_begin_arg / arg); 318 319 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg); 320 } 321 322 if (err) 323 return 4; 324 325 /* Step 5: check channel list if it exists */ 326 if (cmd->chanlist && cmd->chanlist_len > 0) 327 err |= pci9111_ai_check_chanlist(dev, s, cmd); 328 329 if (err) 330 return 5; 331 332 return 0; 333 } 334 pci9111_ai_do_cmd(struct comedi_device * dev,struct comedi_subdevice * s)335 static int pci9111_ai_do_cmd(struct comedi_device *dev, 336 struct comedi_subdevice *s) 337 { 338 struct pci9111_private_data *dev_private = dev->private; 339 struct comedi_cmd *cmd = &s->async->cmd; 340 unsigned int last_chan = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]); 341 unsigned int range0 = CR_RANGE(cmd->chanlist[0]); 342 unsigned int trig = 0; 343 344 /* Set channel scan limit */ 345 /* PCI9111 allows only scanning from channel 0 to channel n */ 346 /* TODO: handle the case of an external multiplexer */ 347 348 if (cmd->chanlist_len > 1) 349 trig |= PCI9111_AI_TRIG_CTRL_ASCAN; 350 351 outb(last_chan, dev->iobase + PCI9111_AI_CHANNEL_REG); 352 353 /* Set gain - all channels use the same range */ 354 outb(PCI9111_AI_RANGE(range0), dev->iobase + PCI9111_AI_RANGE_STAT_REG); 355 356 /* Set timer pacer */ 357 dev_private->scan_delay = 0; 358 if (cmd->convert_src == TRIG_TIMER) { 359 trig |= PCI9111_AI_TRIG_CTRL_TPST; 360 comedi_8254_update_divisors(dev->pacer); 361 comedi_8254_pacer_enable(dev->pacer, 1, 2, true); 362 pci9111_fifo_reset(dev); 363 pci9111_interrupt_source_set(dev, irq_on_fifo_half_full, 364 irq_on_timer_tick); 365 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, 366 false, true, true); 367 368 if (cmd->scan_begin_src == TRIG_TIMER) { 369 dev_private->scan_delay = (cmd->scan_begin_arg / 370 (cmd->convert_arg * cmd->chanlist_len)) - 1; 371 } 372 } else { /* TRIG_EXT */ 373 trig |= PCI9111_AI_TRIG_CTRL_ETIS; 374 pci9111_fifo_reset(dev); 375 pci9111_interrupt_source_set(dev, irq_on_fifo_half_full, 376 irq_on_timer_tick); 377 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, 378 false, true, true); 379 } 380 outb(trig, dev->iobase + PCI9111_AI_TRIG_CTRL_REG); 381 382 dev_private->chunk_counter = 0; 383 dev_private->chunk_num_samples = cmd->chanlist_len * 384 (1 + dev_private->scan_delay); 385 386 return 0; 387 } 388 pci9111_ai_munge(struct comedi_device * dev,struct comedi_subdevice * s,void * data,unsigned int num_bytes,unsigned int start_chan_index)389 static void pci9111_ai_munge(struct comedi_device *dev, 390 struct comedi_subdevice *s, void *data, 391 unsigned int num_bytes, 392 unsigned int start_chan_index) 393 { 394 unsigned short *array = data; 395 unsigned int maxdata = s->maxdata; 396 unsigned int invert = (maxdata + 1) >> 1; 397 unsigned int shift = (maxdata == 0xffff) ? 0 : 4; 398 unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes); 399 unsigned int i; 400 401 for (i = 0; i < num_samples; i++) 402 array[i] = ((array[i] >> shift) & maxdata) ^ invert; 403 } 404 pci9111_handle_fifo_half_full(struct comedi_device * dev,struct comedi_subdevice * s)405 static void pci9111_handle_fifo_half_full(struct comedi_device *dev, 406 struct comedi_subdevice *s) 407 { 408 struct pci9111_private_data *devpriv = dev->private; 409 struct comedi_cmd *cmd = &s->async->cmd; 410 unsigned short *buf = devpriv->ai_bounce_buffer; 411 unsigned int samples; 412 413 samples = comedi_nsamples_left(s, PCI9111_FIFO_HALF_SIZE); 414 insw(dev->iobase + PCI9111_AI_FIFO_REG, buf, samples); 415 416 if (devpriv->scan_delay < 1) { 417 comedi_buf_write_samples(s, buf, samples); 418 } else { 419 unsigned int pos = 0; 420 unsigned int to_read; 421 422 while (pos < samples) { 423 if (devpriv->chunk_counter < cmd->chanlist_len) { 424 to_read = cmd->chanlist_len - 425 devpriv->chunk_counter; 426 427 if (to_read > samples - pos) 428 to_read = samples - pos; 429 430 comedi_buf_write_samples(s, buf + pos, to_read); 431 } else { 432 to_read = devpriv->chunk_num_samples - 433 devpriv->chunk_counter; 434 435 if (to_read > samples - pos) 436 to_read = samples - pos; 437 } 438 439 pos += to_read; 440 devpriv->chunk_counter += to_read; 441 442 if (devpriv->chunk_counter >= 443 devpriv->chunk_num_samples) 444 devpriv->chunk_counter = 0; 445 } 446 } 447 } 448 pci9111_interrupt(int irq,void * p_device)449 static irqreturn_t pci9111_interrupt(int irq, void *p_device) 450 { 451 struct comedi_device *dev = p_device; 452 struct pci9111_private_data *dev_private = dev->private; 453 struct comedi_subdevice *s = dev->read_subdev; 454 struct comedi_async *async; 455 struct comedi_cmd *cmd; 456 unsigned int status; 457 unsigned long irq_flags; 458 unsigned char intcsr; 459 460 if (!dev->attached) { 461 /* Ignore interrupt before device fully attached. */ 462 /* Might not even have allocated subdevices yet! */ 463 return IRQ_NONE; 464 } 465 466 async = s->async; 467 cmd = &async->cmd; 468 469 spin_lock_irqsave(&dev->spinlock, irq_flags); 470 471 /* Check if we are source of interrupt */ 472 intcsr = inb(dev_private->lcr_io_base + PLX9052_INTCSR); 473 if (!(((intcsr & PLX9052_INTCSR_PCIENAB) != 0) && 474 (((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) || 475 ((intcsr & PCI9111_LI2_ACTIVE) == PCI9111_LI2_ACTIVE)))) { 476 /* Not the source of the interrupt. */ 477 /* (N.B. not using PLX9052_INTCSR_SOFTINT) */ 478 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 479 return IRQ_NONE; 480 } 481 482 if ((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) { 483 /* Interrupt comes from fifo_half-full signal */ 484 485 status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG); 486 487 /* '0' means FIFO is full, data may have been lost */ 488 if (!(status & PCI9111_AI_STAT_FF_FF)) { 489 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 490 dev_dbg(dev->class_dev, "fifo overflow\n"); 491 outb(0, dev->iobase + PCI9111_INT_CLR_REG); 492 async->events |= COMEDI_CB_ERROR; 493 comedi_handle_events(dev, s); 494 495 return IRQ_HANDLED; 496 } 497 498 /* '0' means FIFO is half-full */ 499 if (!(status & PCI9111_AI_STAT_FF_HF)) 500 pci9111_handle_fifo_half_full(dev, s); 501 } 502 503 if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) 504 async->events |= COMEDI_CB_EOA; 505 506 outb(0, dev->iobase + PCI9111_INT_CLR_REG); 507 508 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 509 510 comedi_handle_events(dev, s); 511 512 return IRQ_HANDLED; 513 } 514 pci9111_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)515 static int pci9111_ai_eoc(struct comedi_device *dev, 516 struct comedi_subdevice *s, 517 struct comedi_insn *insn, 518 unsigned long context) 519 { 520 unsigned int status; 521 522 status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG); 523 if (status & PCI9111_AI_STAT_FF_EF) 524 return 0; 525 return -EBUSY; 526 } 527 pci9111_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)528 static int pci9111_ai_insn_read(struct comedi_device *dev, 529 struct comedi_subdevice *s, 530 struct comedi_insn *insn, unsigned int *data) 531 { 532 unsigned int chan = CR_CHAN(insn->chanspec); 533 unsigned int range = CR_RANGE(insn->chanspec); 534 unsigned int maxdata = s->maxdata; 535 unsigned int invert = (maxdata + 1) >> 1; 536 unsigned int shift = (maxdata == 0xffff) ? 0 : 4; 537 unsigned int status; 538 int ret; 539 int i; 540 541 outb(chan, dev->iobase + PCI9111_AI_CHANNEL_REG); 542 543 status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG); 544 if ((status & PCI9111_AI_RANGE_MASK) != range) { 545 outb(PCI9111_AI_RANGE(range), 546 dev->iobase + PCI9111_AI_RANGE_STAT_REG); 547 } 548 549 pci9111_fifo_reset(dev); 550 551 for (i = 0; i < insn->n; i++) { 552 /* Generate a software trigger */ 553 outb(0, dev->iobase + PCI9111_SOFT_TRIG_REG); 554 555 ret = comedi_timeout(dev, s, insn, pci9111_ai_eoc, 0); 556 if (ret) { 557 pci9111_fifo_reset(dev); 558 return ret; 559 } 560 561 data[i] = inw(dev->iobase + PCI9111_AI_FIFO_REG); 562 data[i] = ((data[i] >> shift) & maxdata) ^ invert; 563 } 564 565 return i; 566 } 567 pci9111_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)568 static int pci9111_ao_insn_write(struct comedi_device *dev, 569 struct comedi_subdevice *s, 570 struct comedi_insn *insn, 571 unsigned int *data) 572 { 573 unsigned int chan = CR_CHAN(insn->chanspec); 574 unsigned int val = s->readback[chan]; 575 int i; 576 577 for (i = 0; i < insn->n; i++) { 578 val = data[i]; 579 outw(val, dev->iobase + PCI9111_AO_REG); 580 } 581 s->readback[chan] = val; 582 583 return insn->n; 584 } 585 pci9111_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)586 static int pci9111_di_insn_bits(struct comedi_device *dev, 587 struct comedi_subdevice *s, 588 struct comedi_insn *insn, 589 unsigned int *data) 590 { 591 data[1] = inw(dev->iobase + PCI9111_DIO_REG); 592 593 return insn->n; 594 } 595 pci9111_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)596 static int pci9111_do_insn_bits(struct comedi_device *dev, 597 struct comedi_subdevice *s, 598 struct comedi_insn *insn, 599 unsigned int *data) 600 { 601 if (comedi_dio_update_state(s, data)) 602 outw(s->state, dev->iobase + PCI9111_DIO_REG); 603 604 data[1] = s->state; 605 606 return insn->n; 607 } 608 pci9111_reset(struct comedi_device * dev)609 static int pci9111_reset(struct comedi_device *dev) 610 { 611 struct pci9111_private_data *dev_private = dev->private; 612 613 /* Set trigger source to software */ 614 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true, 615 true, false); 616 617 /* disable A/D triggers (software trigger mode) and auto scan off */ 618 outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG); 619 620 return 0; 621 } 622 pci9111_auto_attach(struct comedi_device * dev,unsigned long context_unused)623 static int pci9111_auto_attach(struct comedi_device *dev, 624 unsigned long context_unused) 625 { 626 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 627 struct pci9111_private_data *dev_private; 628 struct comedi_subdevice *s; 629 int ret; 630 631 dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private)); 632 if (!dev_private) 633 return -ENOMEM; 634 635 ret = comedi_pci_enable(dev); 636 if (ret) 637 return ret; 638 dev_private->lcr_io_base = pci_resource_start(pcidev, 1); 639 dev->iobase = pci_resource_start(pcidev, 2); 640 641 pci9111_reset(dev); 642 643 if (pcidev->irq) { 644 ret = request_irq(pcidev->irq, pci9111_interrupt, 645 IRQF_SHARED, dev->board_name, dev); 646 if (ret == 0) 647 dev->irq = pcidev->irq; 648 } 649 650 dev->pacer = comedi_8254_init(dev->iobase + PCI9111_8254_BASE_REG, 651 I8254_OSC_BASE_2MHZ, I8254_IO16, 0); 652 if (!dev->pacer) 653 return -ENOMEM; 654 655 ret = comedi_alloc_subdevices(dev, 4); 656 if (ret) 657 return ret; 658 659 s = &dev->subdevices[0]; 660 s->type = COMEDI_SUBD_AI; 661 s->subdev_flags = SDF_READABLE | SDF_COMMON; 662 s->n_chan = 16; 663 s->maxdata = 0xffff; 664 s->range_table = &pci9111_ai_range; 665 s->insn_read = pci9111_ai_insn_read; 666 if (dev->irq) { 667 dev->read_subdev = s; 668 s->subdev_flags |= SDF_CMD_READ; 669 s->len_chanlist = s->n_chan; 670 s->do_cmdtest = pci9111_ai_do_cmd_test; 671 s->do_cmd = pci9111_ai_do_cmd; 672 s->cancel = pci9111_ai_cancel; 673 s->munge = pci9111_ai_munge; 674 } 675 676 s = &dev->subdevices[1]; 677 s->type = COMEDI_SUBD_AO; 678 s->subdev_flags = SDF_WRITABLE | SDF_COMMON; 679 s->n_chan = 1; 680 s->maxdata = 0x0fff; 681 s->len_chanlist = 1; 682 s->range_table = &range_bipolar10; 683 s->insn_write = pci9111_ao_insn_write; 684 685 ret = comedi_alloc_subdev_readback(s); 686 if (ret) 687 return ret; 688 689 s = &dev->subdevices[2]; 690 s->type = COMEDI_SUBD_DI; 691 s->subdev_flags = SDF_READABLE; 692 s->n_chan = 16; 693 s->maxdata = 1; 694 s->range_table = &range_digital; 695 s->insn_bits = pci9111_di_insn_bits; 696 697 s = &dev->subdevices[3]; 698 s->type = COMEDI_SUBD_DO; 699 s->subdev_flags = SDF_WRITABLE; 700 s->n_chan = 16; 701 s->maxdata = 1; 702 s->range_table = &range_digital; 703 s->insn_bits = pci9111_do_insn_bits; 704 705 return 0; 706 } 707 pci9111_detach(struct comedi_device * dev)708 static void pci9111_detach(struct comedi_device *dev) 709 { 710 if (dev->iobase) 711 pci9111_reset(dev); 712 comedi_pci_detach(dev); 713 } 714 715 static struct comedi_driver adl_pci9111_driver = { 716 .driver_name = "adl_pci9111", 717 .module = THIS_MODULE, 718 .auto_attach = pci9111_auto_attach, 719 .detach = pci9111_detach, 720 }; 721 pci9111_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)722 static int pci9111_pci_probe(struct pci_dev *dev, 723 const struct pci_device_id *id) 724 { 725 return comedi_pci_auto_config(dev, &adl_pci9111_driver, 726 id->driver_data); 727 } 728 729 static const struct pci_device_id pci9111_pci_table[] = { 730 { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x9111) }, 731 /* { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI9111_HG_DEVICE_ID) }, */ 732 { 0 } 733 }; 734 MODULE_DEVICE_TABLE(pci, pci9111_pci_table); 735 736 static struct pci_driver adl_pci9111_pci_driver = { 737 .name = "adl_pci9111", 738 .id_table = pci9111_pci_table, 739 .probe = pci9111_pci_probe, 740 .remove = comedi_pci_auto_unconfig, 741 }; 742 module_comedi_pci_driver(adl_pci9111_driver, adl_pci9111_pci_driver); 743 744 MODULE_AUTHOR("Comedi https://www.comedi.org"); 745 MODULE_DESCRIPTION("Comedi low-level driver"); 746 MODULE_LICENSE("GPL"); 747