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