1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * comedi/drivers/amplc_dio200_common.c 4 * 5 * Common support code for "amplc_dio200" and "amplc_dio200_pci". 6 * 7 * Copyright (C) 2005-2013 MEV Ltd. <https://www.mev.co.uk/> 8 * 9 * COMEDI - Linux Control and Measurement Device Interface 10 * Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org> 11 */ 12 13 #include <linux/module.h> 14 #include <linux/interrupt.h> 15 16 #include "../comedidev.h" 17 18 #include "amplc_dio200.h" 19 #include "comedi_8254.h" 20 #include "8255.h" /* only for register defines */ 21 22 /* 200 series registers */ 23 #define DIO200_IO_SIZE 0x20 24 #define DIO200_PCIE_IO_SIZE 0x4000 25 #define DIO200_CLK_SCE(x) (0x18 + (x)) /* Group X/Y/Z clock sel reg */ 26 #define DIO200_GAT_SCE(x) (0x1b + (x)) /* Group X/Y/Z gate sel reg */ 27 #define DIO200_INT_SCE 0x1e /* Interrupt enable/status register */ 28 /* Extra registers for new PCIe boards */ 29 #define DIO200_ENHANCE 0x20 /* 1 to enable enhanced features */ 30 #define DIO200_VERSION 0x24 /* Hardware version register */ 31 #define DIO200_TS_CONFIG 0x600 /* Timestamp timer config register */ 32 #define DIO200_TS_COUNT 0x602 /* Timestamp timer count register */ 33 34 /* 35 * Functions for constructing value for DIO_200_?CLK_SCE and 36 * DIO_200_?GAT_SCE registers: 37 * 38 * 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2. 39 * 'chan' is the channel: 0, 1 or 2. 40 * 'source' is the signal source: 0 to 7, or 0 to 31 for "enhanced" boards. 41 */ 42 static unsigned char clk_gat_sce(unsigned int which, unsigned int chan, 43 unsigned int source) 44 { 45 return (which << 5) | (chan << 3) | 46 ((source & 030) << 3) | (source & 007); 47 } 48 49 /* 50 * Periods of the internal clock sources in nanoseconds. 51 */ 52 static const unsigned int clock_period[32] = { 53 [1] = 100, /* 10 MHz */ 54 [2] = 1000, /* 1 MHz */ 55 [3] = 10000, /* 100 kHz */ 56 [4] = 100000, /* 10 kHz */ 57 [5] = 1000000, /* 1 kHz */ 58 [11] = 50, /* 20 MHz (enhanced boards) */ 59 /* clock sources 12 and later reserved for enhanced boards */ 60 }; 61 62 /* 63 * Timestamp timer configuration register (for new PCIe boards). 64 */ 65 #define TS_CONFIG_RESET 0x100 /* Reset counter to zero. */ 66 #define TS_CONFIG_CLK_SRC_MASK 0x0FF /* Clock source. */ 67 #define TS_CONFIG_MAX_CLK_SRC 2 /* Maximum clock source value. */ 68 69 /* 70 * Periods of the timestamp timer clock sources in nanoseconds. 71 */ 72 static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = { 73 1, /* 1 nanosecond (but with 20 ns granularity). */ 74 1000, /* 1 microsecond. */ 75 1000000, /* 1 millisecond. */ 76 }; 77 78 struct dio200_subdev_8255 { 79 unsigned int ofs; /* DIO base offset */ 80 }; 81 82 struct dio200_subdev_intr { 83 spinlock_t spinlock; /* protects the 'active' flag */ 84 unsigned int ofs; 85 unsigned int valid_isns; 86 unsigned int enabled_isns; 87 unsigned int active:1; 88 }; 89 90 static unsigned char dio200_read8(struct comedi_device *dev, 91 unsigned int offset) 92 { 93 const struct dio200_board *board = dev->board_ptr; 94 95 if (board->is_pcie) 96 offset <<= 3; 97 98 if (dev->mmio) 99 return readb(dev->mmio + offset); 100 return inb(dev->iobase + offset); 101 } 102 103 static void dio200_write8(struct comedi_device *dev, 104 unsigned int offset, unsigned char val) 105 { 106 const struct dio200_board *board = dev->board_ptr; 107 108 if (board->is_pcie) 109 offset <<= 3; 110 111 if (dev->mmio) 112 writeb(val, dev->mmio + offset); 113 else 114 outb(val, dev->iobase + offset); 115 } 116 117 static unsigned int dio200_read32(struct comedi_device *dev, 118 unsigned int offset) 119 { 120 const struct dio200_board *board = dev->board_ptr; 121 122 if (board->is_pcie) 123 offset <<= 3; 124 125 if (dev->mmio) 126 return readl(dev->mmio + offset); 127 return inl(dev->iobase + offset); 128 } 129 130 static void dio200_write32(struct comedi_device *dev, 131 unsigned int offset, unsigned int val) 132 { 133 const struct dio200_board *board = dev->board_ptr; 134 135 if (board->is_pcie) 136 offset <<= 3; 137 138 if (dev->mmio) 139 writel(val, dev->mmio + offset); 140 else 141 outl(val, dev->iobase + offset); 142 } 143 144 static unsigned int dio200_subdev_8254_offset(struct comedi_device *dev, 145 struct comedi_subdevice *s) 146 { 147 const struct dio200_board *board = dev->board_ptr; 148 struct comedi_8254 *i8254 = s->private; 149 unsigned int offset; 150 151 /* get the offset that was passed to comedi_8254_*_init() */ 152 if (dev->mmio) 153 offset = i8254->mmio - dev->mmio; 154 else 155 offset = i8254->iobase - dev->iobase; 156 157 /* remove the shift that was added for PCIe boards */ 158 if (board->is_pcie) 159 offset >>= 3; 160 161 /* this offset now works for the dio200_{read,write} helpers */ 162 return offset; 163 } 164 165 static int dio200_subdev_intr_insn_bits(struct comedi_device *dev, 166 struct comedi_subdevice *s, 167 struct comedi_insn *insn, 168 unsigned int *data) 169 { 170 const struct dio200_board *board = dev->board_ptr; 171 struct dio200_subdev_intr *subpriv = s->private; 172 173 if (board->has_int_sce) { 174 /* Just read the interrupt status register. */ 175 data[1] = dio200_read8(dev, subpriv->ofs) & subpriv->valid_isns; 176 } else { 177 /* No interrupt status register. */ 178 data[0] = 0; 179 } 180 181 return insn->n; 182 } 183 184 static void dio200_stop_intr(struct comedi_device *dev, 185 struct comedi_subdevice *s) 186 { 187 const struct dio200_board *board = dev->board_ptr; 188 struct dio200_subdev_intr *subpriv = s->private; 189 190 subpriv->active = false; 191 subpriv->enabled_isns = 0; 192 if (board->has_int_sce) 193 dio200_write8(dev, subpriv->ofs, 0); 194 } 195 196 static void dio200_start_intr(struct comedi_device *dev, 197 struct comedi_subdevice *s) 198 { 199 const struct dio200_board *board = dev->board_ptr; 200 struct dio200_subdev_intr *subpriv = s->private; 201 struct comedi_cmd *cmd = &s->async->cmd; 202 unsigned int n; 203 unsigned int isn_bits; 204 205 /* Determine interrupt sources to enable. */ 206 isn_bits = 0; 207 if (cmd->chanlist) { 208 for (n = 0; n < cmd->chanlist_len; n++) 209 isn_bits |= (1U << CR_CHAN(cmd->chanlist[n])); 210 } 211 isn_bits &= subpriv->valid_isns; 212 /* Enable interrupt sources. */ 213 subpriv->enabled_isns = isn_bits; 214 if (board->has_int_sce) 215 dio200_write8(dev, subpriv->ofs, isn_bits); 216 } 217 218 static int dio200_inttrig_start_intr(struct comedi_device *dev, 219 struct comedi_subdevice *s, 220 unsigned int trig_num) 221 { 222 struct dio200_subdev_intr *subpriv = s->private; 223 struct comedi_cmd *cmd = &s->async->cmd; 224 unsigned long flags; 225 226 if (trig_num != cmd->start_arg) 227 return -EINVAL; 228 229 spin_lock_irqsave(&subpriv->spinlock, flags); 230 s->async->inttrig = NULL; 231 if (subpriv->active) 232 dio200_start_intr(dev, s); 233 234 spin_unlock_irqrestore(&subpriv->spinlock, flags); 235 236 return 1; 237 } 238 239 static void dio200_read_scan_intr(struct comedi_device *dev, 240 struct comedi_subdevice *s, 241 unsigned int triggered) 242 { 243 struct comedi_cmd *cmd = &s->async->cmd; 244 unsigned short val; 245 unsigned int n, ch; 246 247 val = 0; 248 for (n = 0; n < cmd->chanlist_len; n++) { 249 ch = CR_CHAN(cmd->chanlist[n]); 250 if (triggered & (1U << ch)) 251 val |= (1U << n); 252 } 253 254 comedi_buf_write_samples(s, &val, 1); 255 256 if (cmd->stop_src == TRIG_COUNT && 257 s->async->scans_done >= cmd->stop_arg) 258 s->async->events |= COMEDI_CB_EOA; 259 } 260 261 static int dio200_handle_read_intr(struct comedi_device *dev, 262 struct comedi_subdevice *s) 263 { 264 const struct dio200_board *board = dev->board_ptr; 265 struct dio200_subdev_intr *subpriv = s->private; 266 unsigned int triggered; 267 unsigned int intstat; 268 unsigned int cur_enabled; 269 unsigned long flags; 270 271 triggered = 0; 272 273 spin_lock_irqsave(&subpriv->spinlock, flags); 274 if (board->has_int_sce) { 275 /* 276 * Collect interrupt sources that have triggered and disable 277 * them temporarily. Loop around until no extra interrupt 278 * sources have triggered, at which point, the valid part of 279 * the interrupt status register will read zero, clearing the 280 * cause of the interrupt. 281 * 282 * Mask off interrupt sources already seen to avoid infinite 283 * loop in case of misconfiguration. 284 */ 285 cur_enabled = subpriv->enabled_isns; 286 while ((intstat = (dio200_read8(dev, subpriv->ofs) & 287 subpriv->valid_isns & ~triggered)) != 0) { 288 triggered |= intstat; 289 cur_enabled &= ~triggered; 290 dio200_write8(dev, subpriv->ofs, cur_enabled); 291 } 292 } else { 293 /* 294 * No interrupt status register. Assume the single interrupt 295 * source has triggered. 296 */ 297 triggered = subpriv->enabled_isns; 298 } 299 300 if (triggered) { 301 /* 302 * Some interrupt sources have triggered and have been 303 * temporarily disabled to clear the cause of the interrupt. 304 * 305 * Reenable them NOW to minimize the time they are disabled. 306 */ 307 cur_enabled = subpriv->enabled_isns; 308 if (board->has_int_sce) 309 dio200_write8(dev, subpriv->ofs, cur_enabled); 310 311 if (subpriv->active) { 312 /* 313 * The command is still active. 314 * 315 * Ignore interrupt sources that the command isn't 316 * interested in (just in case there's a race 317 * condition). 318 */ 319 if (triggered & subpriv->enabled_isns) { 320 /* Collect scan data. */ 321 dio200_read_scan_intr(dev, s, triggered); 322 } 323 } 324 } 325 spin_unlock_irqrestore(&subpriv->spinlock, flags); 326 327 comedi_handle_events(dev, s); 328 329 return (triggered != 0); 330 } 331 332 static int dio200_subdev_intr_cancel(struct comedi_device *dev, 333 struct comedi_subdevice *s) 334 { 335 struct dio200_subdev_intr *subpriv = s->private; 336 unsigned long flags; 337 338 spin_lock_irqsave(&subpriv->spinlock, flags); 339 if (subpriv->active) 340 dio200_stop_intr(dev, s); 341 342 spin_unlock_irqrestore(&subpriv->spinlock, flags); 343 344 return 0; 345 } 346 347 static int dio200_subdev_intr_cmdtest(struct comedi_device *dev, 348 struct comedi_subdevice *s, 349 struct comedi_cmd *cmd) 350 { 351 int err = 0; 352 353 /* Step 1 : check if triggers are trivially valid */ 354 355 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT); 356 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); 357 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW); 358 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 359 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 360 361 if (err) 362 return 1; 363 364 /* Step 2a : make sure trigger sources are unique */ 365 366 err |= comedi_check_trigger_is_unique(cmd->start_src); 367 err |= comedi_check_trigger_is_unique(cmd->stop_src); 368 369 /* Step 2b : and mutually compatible */ 370 371 if (err) 372 return 2; 373 374 /* Step 3: check if arguments are trivially valid */ 375 376 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); 377 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 378 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); 379 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, 380 cmd->chanlist_len); 381 382 if (cmd->stop_src == TRIG_COUNT) 383 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); 384 else /* TRIG_NONE */ 385 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); 386 387 if (err) 388 return 3; 389 390 /* step 4: fix up any arguments */ 391 392 /* if (err) return 4; */ 393 394 return 0; 395 } 396 397 static int dio200_subdev_intr_cmd(struct comedi_device *dev, 398 struct comedi_subdevice *s) 399 { 400 struct comedi_cmd *cmd = &s->async->cmd; 401 struct dio200_subdev_intr *subpriv = s->private; 402 unsigned long flags; 403 404 spin_lock_irqsave(&subpriv->spinlock, flags); 405 406 subpriv->active = true; 407 408 if (cmd->start_src == TRIG_INT) 409 s->async->inttrig = dio200_inttrig_start_intr; 410 else /* TRIG_NOW */ 411 dio200_start_intr(dev, s); 412 413 spin_unlock_irqrestore(&subpriv->spinlock, flags); 414 415 return 0; 416 } 417 418 static int dio200_subdev_intr_init(struct comedi_device *dev, 419 struct comedi_subdevice *s, 420 unsigned int offset, 421 unsigned int valid_isns) 422 { 423 const struct dio200_board *board = dev->board_ptr; 424 struct dio200_subdev_intr *subpriv; 425 426 subpriv = comedi_alloc_spriv(s, sizeof(*subpriv)); 427 if (!subpriv) 428 return -ENOMEM; 429 430 subpriv->ofs = offset; 431 subpriv->valid_isns = valid_isns; 432 spin_lock_init(&subpriv->spinlock); 433 434 if (board->has_int_sce) 435 /* Disable interrupt sources. */ 436 dio200_write8(dev, subpriv->ofs, 0); 437 438 s->type = COMEDI_SUBD_DI; 439 s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_PACKED; 440 if (board->has_int_sce) { 441 s->n_chan = DIO200_MAX_ISNS; 442 s->len_chanlist = DIO200_MAX_ISNS; 443 } else { 444 /* No interrupt source register. Support single channel. */ 445 s->n_chan = 1; 446 s->len_chanlist = 1; 447 } 448 s->range_table = &range_digital; 449 s->maxdata = 1; 450 s->insn_bits = dio200_subdev_intr_insn_bits; 451 s->do_cmdtest = dio200_subdev_intr_cmdtest; 452 s->do_cmd = dio200_subdev_intr_cmd; 453 s->cancel = dio200_subdev_intr_cancel; 454 455 return 0; 456 } 457 458 static irqreturn_t dio200_interrupt(int irq, void *d) 459 { 460 struct comedi_device *dev = d; 461 struct comedi_subdevice *s = dev->read_subdev; 462 int handled; 463 464 if (!dev->attached) 465 return IRQ_NONE; 466 467 handled = dio200_handle_read_intr(dev, s); 468 469 return IRQ_RETVAL(handled); 470 } 471 472 static void dio200_subdev_8254_set_gate_src(struct comedi_device *dev, 473 struct comedi_subdevice *s, 474 unsigned int chan, 475 unsigned int src) 476 { 477 unsigned int offset = dio200_subdev_8254_offset(dev, s); 478 479 dio200_write8(dev, DIO200_GAT_SCE(offset >> 3), 480 clk_gat_sce((offset >> 2) & 1, chan, src)); 481 } 482 483 static void dio200_subdev_8254_set_clock_src(struct comedi_device *dev, 484 struct comedi_subdevice *s, 485 unsigned int chan, 486 unsigned int src) 487 { 488 unsigned int offset = dio200_subdev_8254_offset(dev, s); 489 490 dio200_write8(dev, DIO200_CLK_SCE(offset >> 3), 491 clk_gat_sce((offset >> 2) & 1, chan, src)); 492 } 493 494 static int dio200_subdev_8254_config(struct comedi_device *dev, 495 struct comedi_subdevice *s, 496 struct comedi_insn *insn, 497 unsigned int *data) 498 { 499 const struct dio200_board *board = dev->board_ptr; 500 struct comedi_8254 *i8254 = s->private; 501 unsigned int chan = CR_CHAN(insn->chanspec); 502 unsigned int max_src = board->is_pcie ? 31 : 7; 503 unsigned int src; 504 505 if (!board->has_clk_gat_sce) 506 return -EINVAL; 507 508 switch (data[0]) { 509 case INSN_CONFIG_SET_GATE_SRC: 510 src = data[2]; 511 if (src > max_src) 512 return -EINVAL; 513 514 dio200_subdev_8254_set_gate_src(dev, s, chan, src); 515 i8254->gate_src[chan] = src; 516 break; 517 case INSN_CONFIG_GET_GATE_SRC: 518 data[2] = i8254->gate_src[chan]; 519 break; 520 case INSN_CONFIG_SET_CLOCK_SRC: 521 src = data[1]; 522 if (src > max_src) 523 return -EINVAL; 524 525 dio200_subdev_8254_set_clock_src(dev, s, chan, src); 526 i8254->clock_src[chan] = src; 527 break; 528 case INSN_CONFIG_GET_CLOCK_SRC: 529 data[1] = i8254->clock_src[chan]; 530 data[2] = clock_period[i8254->clock_src[chan]]; 531 break; 532 default: 533 return -EINVAL; 534 } 535 536 return insn->n; 537 } 538 539 static int dio200_subdev_8254_init(struct comedi_device *dev, 540 struct comedi_subdevice *s, 541 unsigned int offset) 542 { 543 const struct dio200_board *board = dev->board_ptr; 544 struct comedi_8254 *i8254; 545 unsigned int regshift; 546 int chan; 547 548 /* 549 * PCIe boards need the offset shifted in order to get the 550 * correct base address of the timer. 551 */ 552 if (board->is_pcie) { 553 offset <<= 3; 554 regshift = 3; 555 } else { 556 regshift = 0; 557 } 558 559 if (dev->mmio) { 560 i8254 = comedi_8254_mm_init(dev->mmio + offset, 561 0, I8254_IO8, regshift); 562 } else { 563 i8254 = comedi_8254_init(dev->iobase + offset, 564 0, I8254_IO8, regshift); 565 } 566 if (!i8254) 567 return -ENOMEM; 568 569 comedi_8254_subdevice_init(s, i8254); 570 571 i8254->insn_config = dio200_subdev_8254_config; 572 573 /* 574 * There could be multiple timers so this driver does not 575 * use dev->pacer to save the i8254 pointer. Instead, 576 * comedi_8254_subdevice_init() saved the i8254 pointer in 577 * s->private. Mark the subdevice as having private data 578 * to be automatically freed when the device is detached. 579 */ 580 comedi_set_spriv_auto_free(s); 581 582 /* Initialize channels. */ 583 if (board->has_clk_gat_sce) { 584 for (chan = 0; chan < 3; chan++) { 585 /* Gate source 0 is VCC (logic 1). */ 586 dio200_subdev_8254_set_gate_src(dev, s, chan, 0); 587 /* Clock source 0 is the dedicated clock input. */ 588 dio200_subdev_8254_set_clock_src(dev, s, chan, 0); 589 } 590 } 591 592 return 0; 593 } 594 595 static void dio200_subdev_8255_set_dir(struct comedi_device *dev, 596 struct comedi_subdevice *s) 597 { 598 struct dio200_subdev_8255 *subpriv = s->private; 599 int config; 600 601 config = I8255_CTRL_CW; 602 /* 1 in io_bits indicates output, 1 in config indicates input */ 603 if (!(s->io_bits & 0x0000ff)) 604 config |= I8255_CTRL_A_IO; 605 if (!(s->io_bits & 0x00ff00)) 606 config |= I8255_CTRL_B_IO; 607 if (!(s->io_bits & 0x0f0000)) 608 config |= I8255_CTRL_C_LO_IO; 609 if (!(s->io_bits & 0xf00000)) 610 config |= I8255_CTRL_C_HI_IO; 611 dio200_write8(dev, subpriv->ofs + I8255_CTRL_REG, config); 612 } 613 614 static int dio200_subdev_8255_bits(struct comedi_device *dev, 615 struct comedi_subdevice *s, 616 struct comedi_insn *insn, 617 unsigned int *data) 618 { 619 struct dio200_subdev_8255 *subpriv = s->private; 620 unsigned int mask; 621 unsigned int val; 622 623 mask = comedi_dio_update_state(s, data); 624 if (mask) { 625 if (mask & 0xff) { 626 dio200_write8(dev, subpriv->ofs + I8255_DATA_A_REG, 627 s->state & 0xff); 628 } 629 if (mask & 0xff00) { 630 dio200_write8(dev, subpriv->ofs + I8255_DATA_B_REG, 631 (s->state >> 8) & 0xff); 632 } 633 if (mask & 0xff0000) { 634 dio200_write8(dev, subpriv->ofs + I8255_DATA_C_REG, 635 (s->state >> 16) & 0xff); 636 } 637 } 638 639 val = dio200_read8(dev, subpriv->ofs + I8255_DATA_A_REG); 640 val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_B_REG) << 8; 641 val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_C_REG) << 16; 642 643 data[1] = val; 644 645 return insn->n; 646 } 647 648 static int dio200_subdev_8255_config(struct comedi_device *dev, 649 struct comedi_subdevice *s, 650 struct comedi_insn *insn, 651 unsigned int *data) 652 { 653 unsigned int chan = CR_CHAN(insn->chanspec); 654 unsigned int mask; 655 int ret; 656 657 if (chan < 8) 658 mask = 0x0000ff; 659 else if (chan < 16) 660 mask = 0x00ff00; 661 else if (chan < 20) 662 mask = 0x0f0000; 663 else 664 mask = 0xf00000; 665 666 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 667 if (ret) 668 return ret; 669 670 dio200_subdev_8255_set_dir(dev, s); 671 672 return insn->n; 673 } 674 675 static int dio200_subdev_8255_init(struct comedi_device *dev, 676 struct comedi_subdevice *s, 677 unsigned int offset) 678 { 679 struct dio200_subdev_8255 *subpriv; 680 681 subpriv = comedi_alloc_spriv(s, sizeof(*subpriv)); 682 if (!subpriv) 683 return -ENOMEM; 684 685 subpriv->ofs = offset; 686 687 s->type = COMEDI_SUBD_DIO; 688 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 689 s->n_chan = 24; 690 s->range_table = &range_digital; 691 s->maxdata = 1; 692 s->insn_bits = dio200_subdev_8255_bits; 693 s->insn_config = dio200_subdev_8255_config; 694 dio200_subdev_8255_set_dir(dev, s); 695 return 0; 696 } 697 698 static int dio200_subdev_timer_read(struct comedi_device *dev, 699 struct comedi_subdevice *s, 700 struct comedi_insn *insn, 701 unsigned int *data) 702 { 703 unsigned int n; 704 705 for (n = 0; n < insn->n; n++) 706 data[n] = dio200_read32(dev, DIO200_TS_COUNT); 707 return n; 708 } 709 710 static void dio200_subdev_timer_reset(struct comedi_device *dev, 711 struct comedi_subdevice *s) 712 { 713 unsigned int clock; 714 715 clock = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK; 716 dio200_write32(dev, DIO200_TS_CONFIG, clock | TS_CONFIG_RESET); 717 dio200_write32(dev, DIO200_TS_CONFIG, clock); 718 } 719 720 static void dio200_subdev_timer_get_clock_src(struct comedi_device *dev, 721 struct comedi_subdevice *s, 722 unsigned int *src, 723 unsigned int *period) 724 { 725 unsigned int clk; 726 727 clk = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK; 728 *src = clk; 729 *period = (clk < ARRAY_SIZE(ts_clock_period)) ? 730 ts_clock_period[clk] : 0; 731 } 732 733 static int dio200_subdev_timer_set_clock_src(struct comedi_device *dev, 734 struct comedi_subdevice *s, 735 unsigned int src) 736 { 737 if (src > TS_CONFIG_MAX_CLK_SRC) 738 return -EINVAL; 739 dio200_write32(dev, DIO200_TS_CONFIG, src); 740 return 0; 741 } 742 743 static int dio200_subdev_timer_config(struct comedi_device *dev, 744 struct comedi_subdevice *s, 745 struct comedi_insn *insn, 746 unsigned int *data) 747 { 748 int ret = 0; 749 750 switch (data[0]) { 751 case INSN_CONFIG_RESET: 752 dio200_subdev_timer_reset(dev, s); 753 break; 754 case INSN_CONFIG_SET_CLOCK_SRC: 755 ret = dio200_subdev_timer_set_clock_src(dev, s, data[1]); 756 if (ret < 0) 757 ret = -EINVAL; 758 break; 759 case INSN_CONFIG_GET_CLOCK_SRC: 760 dio200_subdev_timer_get_clock_src(dev, s, &data[1], &data[2]); 761 break; 762 default: 763 ret = -EINVAL; 764 break; 765 } 766 return ret < 0 ? ret : insn->n; 767 } 768 769 void amplc_dio200_set_enhance(struct comedi_device *dev, unsigned char val) 770 { 771 dio200_write8(dev, DIO200_ENHANCE, val); 772 } 773 EXPORT_SYMBOL_GPL(amplc_dio200_set_enhance); 774 775 int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq, 776 unsigned long req_irq_flags) 777 { 778 const struct dio200_board *board = dev->board_ptr; 779 struct comedi_subdevice *s; 780 unsigned int n; 781 int ret; 782 783 ret = comedi_alloc_subdevices(dev, board->n_subdevs); 784 if (ret) 785 return ret; 786 787 for (n = 0; n < dev->n_subdevices; n++) { 788 s = &dev->subdevices[n]; 789 switch (board->sdtype[n]) { 790 case sd_8254: 791 /* counter subdevice (8254) */ 792 ret = dio200_subdev_8254_init(dev, s, 793 board->sdinfo[n]); 794 if (ret < 0) 795 return ret; 796 break; 797 case sd_8255: 798 /* digital i/o subdevice (8255) */ 799 ret = dio200_subdev_8255_init(dev, s, 800 board->sdinfo[n]); 801 if (ret < 0) 802 return ret; 803 break; 804 case sd_intr: 805 /* 'INTERRUPT' subdevice */ 806 if (irq && !dev->read_subdev) { 807 ret = dio200_subdev_intr_init(dev, s, 808 DIO200_INT_SCE, 809 board->sdinfo[n]); 810 if (ret < 0) 811 return ret; 812 dev->read_subdev = s; 813 } else { 814 s->type = COMEDI_SUBD_UNUSED; 815 } 816 break; 817 case sd_timer: 818 s->type = COMEDI_SUBD_TIMER; 819 s->subdev_flags = SDF_READABLE | SDF_LSAMPL; 820 s->n_chan = 1; 821 s->maxdata = 0xffffffff; 822 s->insn_read = dio200_subdev_timer_read; 823 s->insn_config = dio200_subdev_timer_config; 824 break; 825 default: 826 s->type = COMEDI_SUBD_UNUSED; 827 break; 828 } 829 } 830 831 if (irq && dev->read_subdev) { 832 if (request_irq(irq, dio200_interrupt, req_irq_flags, 833 dev->board_name, dev) >= 0) { 834 dev->irq = irq; 835 } else { 836 dev_warn(dev->class_dev, 837 "warning! irq %u unavailable!\n", irq); 838 } 839 } 840 841 return 0; 842 } 843 EXPORT_SYMBOL_GPL(amplc_dio200_common_attach); 844 845 static int __init amplc_dio200_common_init(void) 846 { 847 return 0; 848 } 849 module_init(amplc_dio200_common_init); 850 851 static void __exit amplc_dio200_common_exit(void) 852 { 853 } 854 module_exit(amplc_dio200_common_exit); 855 856 MODULE_AUTHOR("Comedi https://www.comedi.org"); 857 MODULE_DESCRIPTION("Comedi helper for amplc_dio200 and amplc_dio200_pci"); 858 MODULE_LICENSE("GPL"); 859