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