1/* 2 comedi/drivers/amplc_dio200_common.c 3 4 Common support code for "amplc_dio200" and "amplc_dio200_pci". 5 6 Copyright (C) 2005-2013 MEV Ltd. <http://www.mev.co.uk/> 7 8 COMEDI - Linux Control and Measurement Device Interface 9 Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org> 10 11 This program is free software; you can redistribute it and/or modify 12 it under the terms of the GNU General Public License as published by 13 the Free Software Foundation; either version 2 of the License, or 14 (at your option) any later version. 15 16 This program is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 GNU General Public License for more details. 20*/ 21 22#include <linux/module.h> 23#include <linux/interrupt.h> 24 25#include "../comedidev.h" 26 27#include "amplc_dio200.h" 28#include "comedi_fc.h" 29#include "8253.h" 30#include "8255.h" /* only for register defines */ 31 32/* 200 series registers */ 33#define DIO200_IO_SIZE 0x20 34#define DIO200_PCIE_IO_SIZE 0x4000 35#define DIO200_XCLK_SCE 0x18 /* Group X clock selection register */ 36#define DIO200_YCLK_SCE 0x19 /* Group Y clock selection register */ 37#define DIO200_ZCLK_SCE 0x1a /* Group Z clock selection register */ 38#define DIO200_XGAT_SCE 0x1b /* Group X gate selection register */ 39#define DIO200_YGAT_SCE 0x1c /* Group Y gate selection register */ 40#define DIO200_ZGAT_SCE 0x1d /* Group Z gate selection register */ 41#define DIO200_INT_SCE 0x1e /* Interrupt enable/status register */ 42/* Extra registers for new PCIe boards */ 43#define DIO200_ENHANCE 0x20 /* 1 to enable enhanced features */ 44#define DIO200_VERSION 0x24 /* Hardware version register */ 45#define DIO200_TS_CONFIG 0x600 /* Timestamp timer config register */ 46#define DIO200_TS_COUNT 0x602 /* Timestamp timer count register */ 47 48/* 49 * Functions for constructing value for DIO_200_?CLK_SCE and 50 * DIO_200_?GAT_SCE registers: 51 * 52 * 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2. 53 * 'chan' is the channel: 0, 1 or 2. 54 * 'source' is the signal source: 0 to 7, or 0 to 31 for "enhanced" boards. 55 */ 56static unsigned char clk_gat_sce(unsigned int which, unsigned int chan, 57 unsigned int source) 58{ 59 return (which << 5) | (chan << 3) | 60 ((source & 030) << 3) | (source & 007); 61} 62 63static unsigned char clk_sce(unsigned int which, unsigned int chan, 64 unsigned int source) 65{ 66 return clk_gat_sce(which, chan, source); 67} 68 69static unsigned char gat_sce(unsigned int which, unsigned int chan, 70 unsigned int source) 71{ 72 return clk_gat_sce(which, chan, source); 73} 74 75/* 76 * Periods of the internal clock sources in nanoseconds. 77 */ 78static const unsigned int clock_period[32] = { 79 [1] = 100, /* 10 MHz */ 80 [2] = 1000, /* 1 MHz */ 81 [3] = 10000, /* 100 kHz */ 82 [4] = 100000, /* 10 kHz */ 83 [5] = 1000000, /* 1 kHz */ 84 [11] = 50, /* 20 MHz (enhanced boards) */ 85 /* clock sources 12 and later reserved for enhanced boards */ 86}; 87 88/* 89 * Timestamp timer configuration register (for new PCIe boards). 90 */ 91#define TS_CONFIG_RESET 0x100 /* Reset counter to zero. */ 92#define TS_CONFIG_CLK_SRC_MASK 0x0FF /* Clock source. */ 93#define TS_CONFIG_MAX_CLK_SRC 2 /* Maximum clock source value. */ 94 95/* 96 * Periods of the timestamp timer clock sources in nanoseconds. 97 */ 98static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = { 99 1, /* 1 nanosecond (but with 20 ns granularity). */ 100 1000, /* 1 microsecond. */ 101 1000000, /* 1 millisecond. */ 102}; 103 104struct dio200_subdev_8254 { 105 unsigned int ofs; /* Counter base offset */ 106 unsigned int clk_sce_ofs; /* CLK_SCE base address */ 107 unsigned int gat_sce_ofs; /* GAT_SCE base address */ 108 int which; /* Bit 5 of CLK_SCE or GAT_SCE */ 109 unsigned int clock_src[3]; /* Current clock sources */ 110 unsigned int gate_src[3]; /* Current gate sources */ 111 spinlock_t spinlock; 112}; 113 114struct dio200_subdev_8255 { 115 unsigned int ofs; /* DIO base offset */ 116}; 117 118struct dio200_subdev_intr { 119 spinlock_t spinlock; 120 unsigned int ofs; 121 unsigned int valid_isns; 122 unsigned int enabled_isns; 123 unsigned int stopcount; 124 bool active:1; 125}; 126 127static unsigned char dio200_read8(struct comedi_device *dev, 128 unsigned int offset) 129{ 130 const struct dio200_board *board = dev->board_ptr; 131 132 if (board->is_pcie) 133 offset <<= 3; 134 135 if (dev->mmio) 136 return readb(dev->mmio + offset); 137 return inb(dev->iobase + offset); 138} 139 140static void dio200_write8(struct comedi_device *dev, 141 unsigned int offset, unsigned char val) 142{ 143 const struct dio200_board *board = dev->board_ptr; 144 145 if (board->is_pcie) 146 offset <<= 3; 147 148 if (dev->mmio) 149 writeb(val, dev->mmio + offset); 150 else 151 outb(val, dev->iobase + offset); 152} 153 154static unsigned int dio200_read32(struct comedi_device *dev, 155 unsigned int offset) 156{ 157 const struct dio200_board *board = dev->board_ptr; 158 159 if (board->is_pcie) 160 offset <<= 3; 161 162 if (dev->mmio) 163 return readl(dev->mmio + offset); 164 return inl(dev->iobase + offset); 165} 166 167static void dio200_write32(struct comedi_device *dev, 168 unsigned int offset, unsigned int val) 169{ 170 const struct dio200_board *board = dev->board_ptr; 171 172 if (board->is_pcie) 173 offset <<= 3; 174 175 if (dev->mmio) 176 writel(val, dev->mmio + offset); 177 else 178 outl(val, dev->iobase + offset); 179} 180 181static int dio200_subdev_intr_insn_bits(struct comedi_device *dev, 182 struct comedi_subdevice *s, 183 struct comedi_insn *insn, 184 unsigned int *data) 185{ 186 const struct dio200_board *board = dev->board_ptr; 187 struct dio200_subdev_intr *subpriv = s->private; 188 189 if (board->has_int_sce) { 190 /* Just read the interrupt status register. */ 191 data[1] = dio200_read8(dev, subpriv->ofs) & subpriv->valid_isns; 192 } else { 193 /* No interrupt status register. */ 194 data[0] = 0; 195 } 196 197 return insn->n; 198} 199 200static void dio200_stop_intr(struct comedi_device *dev, 201 struct comedi_subdevice *s) 202{ 203 const struct dio200_board *board = dev->board_ptr; 204 struct dio200_subdev_intr *subpriv = s->private; 205 206 subpriv->active = false; 207 subpriv->enabled_isns = 0; 208 if (board->has_int_sce) 209 dio200_write8(dev, subpriv->ofs, 0); 210} 211 212static void dio200_start_intr(struct comedi_device *dev, 213 struct comedi_subdevice *s) 214{ 215 const struct dio200_board *board = dev->board_ptr; 216 struct dio200_subdev_intr *subpriv = s->private; 217 struct comedi_cmd *cmd = &s->async->cmd; 218 unsigned int n; 219 unsigned isn_bits; 220 221 /* Determine interrupt sources to enable. */ 222 isn_bits = 0; 223 if (cmd->chanlist) { 224 for (n = 0; n < cmd->chanlist_len; n++) 225 isn_bits |= (1U << CR_CHAN(cmd->chanlist[n])); 226 } 227 isn_bits &= subpriv->valid_isns; 228 /* Enable interrupt sources. */ 229 subpriv->enabled_isns = isn_bits; 230 if (board->has_int_sce) 231 dio200_write8(dev, subpriv->ofs, isn_bits); 232} 233 234static int dio200_inttrig_start_intr(struct comedi_device *dev, 235 struct comedi_subdevice *s, 236 unsigned int trig_num) 237{ 238 struct dio200_subdev_intr *subpriv = s->private; 239 struct comedi_cmd *cmd = &s->async->cmd; 240 unsigned long flags; 241 242 if (trig_num != cmd->start_arg) 243 return -EINVAL; 244 245 spin_lock_irqsave(&subpriv->spinlock, flags); 246 s->async->inttrig = NULL; 247 if (subpriv->active) 248 dio200_start_intr(dev, s); 249 250 spin_unlock_irqrestore(&subpriv->spinlock, flags); 251 252 return 1; 253} 254 255static void dio200_read_scan_intr(struct comedi_device *dev, 256 struct comedi_subdevice *s, 257 unsigned int triggered) 258{ 259 struct dio200_subdev_intr *subpriv = s->private; 260 struct comedi_cmd *cmd = &s->async->cmd; 261 unsigned short val; 262 unsigned int n, ch; 263 264 val = 0; 265 for (n = 0; n < cmd->chanlist_len; n++) { 266 ch = CR_CHAN(cmd->chanlist[n]); 267 if (triggered & (1U << ch)) 268 val |= (1U << n); 269 } 270 /* Write the scan to the buffer. */ 271 if (comedi_buf_put(s, val)) { 272 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS); 273 } else { 274 /* Error! Stop acquisition. */ 275 dio200_stop_intr(dev, s); 276 s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; 277 dev_err(dev->class_dev, "buffer overflow\n"); 278 } 279 280 /* Check for end of acquisition. */ 281 if (cmd->stop_src == TRIG_COUNT) { 282 if (subpriv->stopcount > 0) { 283 subpriv->stopcount--; 284 if (subpriv->stopcount == 0) { 285 s->async->events |= COMEDI_CB_EOA; 286 dio200_stop_intr(dev, s); 287 } 288 } 289 } 290} 291 292static int dio200_handle_read_intr(struct comedi_device *dev, 293 struct comedi_subdevice *s) 294{ 295 const struct dio200_board *board = dev->board_ptr; 296 struct dio200_subdev_intr *subpriv = s->private; 297 unsigned triggered; 298 unsigned intstat; 299 unsigned cur_enabled; 300 unsigned int oldevents; 301 unsigned long flags; 302 303 triggered = 0; 304 305 spin_lock_irqsave(&subpriv->spinlock, flags); 306 oldevents = s->async->events; 307 if (board->has_int_sce) { 308 /* 309 * Collect interrupt sources that have triggered and disable 310 * them temporarily. Loop around until no extra interrupt 311 * sources have triggered, at which point, the valid part of 312 * the interrupt status register will read zero, clearing the 313 * cause of the interrupt. 314 * 315 * Mask off interrupt sources already seen to avoid infinite 316 * loop in case of misconfiguration. 317 */ 318 cur_enabled = subpriv->enabled_isns; 319 while ((intstat = (dio200_read8(dev, subpriv->ofs) & 320 subpriv->valid_isns & ~triggered)) != 0) { 321 triggered |= intstat; 322 cur_enabled &= ~triggered; 323 dio200_write8(dev, subpriv->ofs, cur_enabled); 324 } 325 } else { 326 /* 327 * No interrupt status register. Assume the single interrupt 328 * source has triggered. 329 */ 330 triggered = subpriv->enabled_isns; 331 } 332 333 if (triggered) { 334 /* 335 * Some interrupt sources have triggered and have been 336 * temporarily disabled to clear the cause of the interrupt. 337 * 338 * Reenable them NOW to minimize the time they are disabled. 339 */ 340 cur_enabled = subpriv->enabled_isns; 341 if (board->has_int_sce) 342 dio200_write8(dev, subpriv->ofs, cur_enabled); 343 344 if (subpriv->active) { 345 /* 346 * The command is still active. 347 * 348 * Ignore interrupt sources that the command isn't 349 * interested in (just in case there's a race 350 * condition). 351 */ 352 if (triggered & subpriv->enabled_isns) 353 /* Collect scan data. */ 354 dio200_read_scan_intr(dev, s, triggered); 355 } 356 } 357 spin_unlock_irqrestore(&subpriv->spinlock, flags); 358 359 if (oldevents != s->async->events) 360 comedi_event(dev, s); 361 362 return (triggered != 0); 363} 364 365static int dio200_subdev_intr_cancel(struct comedi_device *dev, 366 struct comedi_subdevice *s) 367{ 368 struct dio200_subdev_intr *subpriv = s->private; 369 unsigned long flags; 370 371 spin_lock_irqsave(&subpriv->spinlock, flags); 372 if (subpriv->active) 373 dio200_stop_intr(dev, s); 374 375 spin_unlock_irqrestore(&subpriv->spinlock, flags); 376 377 return 0; 378} 379 380static int dio200_subdev_intr_cmdtest(struct comedi_device *dev, 381 struct comedi_subdevice *s, 382 struct comedi_cmd *cmd) 383{ 384 int err = 0; 385 386 /* Step 1 : check if triggers are trivially valid */ 387 388 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT); 389 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); 390 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW); 391 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 392 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 393 394 if (err) 395 return 1; 396 397 /* Step 2a : make sure trigger sources are unique */ 398 399 err |= cfc_check_trigger_is_unique(cmd->start_src); 400 err |= cfc_check_trigger_is_unique(cmd->stop_src); 401 402 /* Step 2b : and mutually compatible */ 403 404 if (err) 405 return 2; 406 407 /* Step 3: check if arguments are trivially valid */ 408 409 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); 410 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 411 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0); 412 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); 413 414 if (cmd->stop_src == TRIG_COUNT) 415 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1); 416 else /* TRIG_NONE */ 417 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); 418 419 if (err) 420 return 3; 421 422 /* step 4: fix up any arguments */ 423 424 /* if (err) return 4; */ 425 426 return 0; 427} 428 429static int dio200_subdev_intr_cmd(struct comedi_device *dev, 430 struct comedi_subdevice *s) 431{ 432 struct comedi_cmd *cmd = &s->async->cmd; 433 struct dio200_subdev_intr *subpriv = s->private; 434 unsigned long flags; 435 436 spin_lock_irqsave(&subpriv->spinlock, flags); 437 438 subpriv->active = true; 439 subpriv->stopcount = cmd->stop_arg; 440 441 if (cmd->start_src == TRIG_INT) 442 s->async->inttrig = dio200_inttrig_start_intr; 443 else /* TRIG_NOW */ 444 dio200_start_intr(dev, s); 445 446 spin_unlock_irqrestore(&subpriv->spinlock, flags); 447 448 return 0; 449} 450 451static int dio200_subdev_intr_init(struct comedi_device *dev, 452 struct comedi_subdevice *s, 453 unsigned int offset, 454 unsigned valid_isns) 455{ 456 const struct dio200_board *board = dev->board_ptr; 457 struct dio200_subdev_intr *subpriv; 458 459 subpriv = comedi_alloc_spriv(s, sizeof(*subpriv)); 460 if (!subpriv) 461 return -ENOMEM; 462 463 subpriv->ofs = offset; 464 subpriv->valid_isns = valid_isns; 465 spin_lock_init(&subpriv->spinlock); 466 467 if (board->has_int_sce) 468 /* Disable interrupt sources. */ 469 dio200_write8(dev, subpriv->ofs, 0); 470 471 s->type = COMEDI_SUBD_DI; 472 s->subdev_flags = SDF_READABLE | SDF_CMD_READ; 473 if (board->has_int_sce) { 474 s->n_chan = DIO200_MAX_ISNS; 475 s->len_chanlist = DIO200_MAX_ISNS; 476 } else { 477 /* No interrupt source register. Support single channel. */ 478 s->n_chan = 1; 479 s->len_chanlist = 1; 480 } 481 s->range_table = &range_digital; 482 s->maxdata = 1; 483 s->insn_bits = dio200_subdev_intr_insn_bits; 484 s->do_cmdtest = dio200_subdev_intr_cmdtest; 485 s->do_cmd = dio200_subdev_intr_cmd; 486 s->cancel = dio200_subdev_intr_cancel; 487 488 return 0; 489} 490 491static irqreturn_t dio200_interrupt(int irq, void *d) 492{ 493 struct comedi_device *dev = d; 494 struct comedi_subdevice *s = dev->read_subdev; 495 int handled; 496 497 if (!dev->attached) 498 return IRQ_NONE; 499 500 handled = dio200_handle_read_intr(dev, s); 501 502 return IRQ_RETVAL(handled); 503} 504 505static unsigned int dio200_subdev_8254_read_chan(struct comedi_device *dev, 506 struct comedi_subdevice *s, 507 unsigned int chan) 508{ 509 struct dio200_subdev_8254 *subpriv = s->private; 510 unsigned int val; 511 512 /* latch counter */ 513 val = chan << 6; 514 dio200_write8(dev, subpriv->ofs + i8254_control_reg, val); 515 /* read lsb, msb */ 516 val = dio200_read8(dev, subpriv->ofs + chan); 517 val += dio200_read8(dev, subpriv->ofs + chan) << 8; 518 return val; 519} 520 521static void dio200_subdev_8254_write_chan(struct comedi_device *dev, 522 struct comedi_subdevice *s, 523 unsigned int chan, 524 unsigned int count) 525{ 526 struct dio200_subdev_8254 *subpriv = s->private; 527 528 /* write lsb, msb */ 529 dio200_write8(dev, subpriv->ofs + chan, count & 0xff); 530 dio200_write8(dev, subpriv->ofs + chan, (count >> 8) & 0xff); 531} 532 533static void dio200_subdev_8254_set_mode(struct comedi_device *dev, 534 struct comedi_subdevice *s, 535 unsigned int chan, 536 unsigned int mode) 537{ 538 struct dio200_subdev_8254 *subpriv = s->private; 539 unsigned int byte; 540 541 byte = chan << 6; 542 byte |= 0x30; /* access order: lsb, msb */ 543 byte |= (mode & 0xf); /* counter mode and BCD|binary */ 544 dio200_write8(dev, subpriv->ofs + i8254_control_reg, byte); 545} 546 547static unsigned int dio200_subdev_8254_status(struct comedi_device *dev, 548 struct comedi_subdevice *s, 549 unsigned int chan) 550{ 551 struct dio200_subdev_8254 *subpriv = s->private; 552 553 /* latch status */ 554 dio200_write8(dev, subpriv->ofs + i8254_control_reg, 555 0xe0 | (2 << chan)); 556 /* read status */ 557 return dio200_read8(dev, subpriv->ofs + chan); 558} 559 560static int dio200_subdev_8254_read(struct comedi_device *dev, 561 struct comedi_subdevice *s, 562 struct comedi_insn *insn, 563 unsigned int *data) 564{ 565 struct dio200_subdev_8254 *subpriv = s->private; 566 int chan = CR_CHAN(insn->chanspec); 567 unsigned int n; 568 unsigned long flags; 569 570 for (n = 0; n < insn->n; n++) { 571 spin_lock_irqsave(&subpriv->spinlock, flags); 572 data[n] = dio200_subdev_8254_read_chan(dev, s, chan); 573 spin_unlock_irqrestore(&subpriv->spinlock, flags); 574 } 575 return insn->n; 576} 577 578static int dio200_subdev_8254_write(struct comedi_device *dev, 579 struct comedi_subdevice *s, 580 struct comedi_insn *insn, 581 unsigned int *data) 582{ 583 struct dio200_subdev_8254 *subpriv = s->private; 584 int chan = CR_CHAN(insn->chanspec); 585 unsigned int n; 586 unsigned long flags; 587 588 for (n = 0; n < insn->n; n++) { 589 spin_lock_irqsave(&subpriv->spinlock, flags); 590 dio200_subdev_8254_write_chan(dev, s, chan, data[n]); 591 spin_unlock_irqrestore(&subpriv->spinlock, flags); 592 } 593 return insn->n; 594} 595 596static int dio200_subdev_8254_set_gate_src(struct comedi_device *dev, 597 struct comedi_subdevice *s, 598 unsigned int counter_number, 599 unsigned int gate_src) 600{ 601 const struct dio200_board *board = dev->board_ptr; 602 struct dio200_subdev_8254 *subpriv = s->private; 603 unsigned char byte; 604 605 if (!board->has_clk_gat_sce) 606 return -1; 607 if (counter_number > 2) 608 return -1; 609 if (gate_src > (board->is_pcie ? 31 : 7)) 610 return -1; 611 612 subpriv->gate_src[counter_number] = gate_src; 613 byte = gat_sce(subpriv->which, counter_number, gate_src); 614 dio200_write8(dev, subpriv->gat_sce_ofs, byte); 615 616 return 0; 617} 618 619static int dio200_subdev_8254_get_gate_src(struct comedi_device *dev, 620 struct comedi_subdevice *s, 621 unsigned int counter_number) 622{ 623 const struct dio200_board *board = dev->board_ptr; 624 struct dio200_subdev_8254 *subpriv = s->private; 625 626 if (!board->has_clk_gat_sce) 627 return -1; 628 if (counter_number > 2) 629 return -1; 630 631 return subpriv->gate_src[counter_number]; 632} 633 634static int dio200_subdev_8254_set_clock_src(struct comedi_device *dev, 635 struct comedi_subdevice *s, 636 unsigned int counter_number, 637 unsigned int clock_src) 638{ 639 const struct dio200_board *board = dev->board_ptr; 640 struct dio200_subdev_8254 *subpriv = s->private; 641 unsigned char byte; 642 643 if (!board->has_clk_gat_sce) 644 return -1; 645 if (counter_number > 2) 646 return -1; 647 if (clock_src > (board->is_pcie ? 31 : 7)) 648 return -1; 649 650 subpriv->clock_src[counter_number] = clock_src; 651 byte = clk_sce(subpriv->which, counter_number, clock_src); 652 dio200_write8(dev, subpriv->clk_sce_ofs, byte); 653 654 return 0; 655} 656 657static int dio200_subdev_8254_get_clock_src(struct comedi_device *dev, 658 struct comedi_subdevice *s, 659 unsigned int counter_number, 660 unsigned int *period_ns) 661{ 662 const struct dio200_board *board = dev->board_ptr; 663 struct dio200_subdev_8254 *subpriv = s->private; 664 unsigned clock_src; 665 666 if (!board->has_clk_gat_sce) 667 return -1; 668 if (counter_number > 2) 669 return -1; 670 671 clock_src = subpriv->clock_src[counter_number]; 672 *period_ns = clock_period[clock_src]; 673 return clock_src; 674} 675 676static int dio200_subdev_8254_config(struct comedi_device *dev, 677 struct comedi_subdevice *s, 678 struct comedi_insn *insn, 679 unsigned int *data) 680{ 681 struct dio200_subdev_8254 *subpriv = s->private; 682 int ret = 0; 683 int chan = CR_CHAN(insn->chanspec); 684 unsigned long flags; 685 686 spin_lock_irqsave(&subpriv->spinlock, flags); 687 switch (data[0]) { 688 case INSN_CONFIG_SET_COUNTER_MODE: 689 if (data[1] > (I8254_MODE5 | I8254_BCD)) 690 ret = -EINVAL; 691 else 692 dio200_subdev_8254_set_mode(dev, s, chan, data[1]); 693 break; 694 case INSN_CONFIG_8254_READ_STATUS: 695 data[1] = dio200_subdev_8254_status(dev, s, chan); 696 break; 697 case INSN_CONFIG_SET_GATE_SRC: 698 ret = dio200_subdev_8254_set_gate_src(dev, s, chan, data[2]); 699 if (ret < 0) 700 ret = -EINVAL; 701 break; 702 case INSN_CONFIG_GET_GATE_SRC: 703 ret = dio200_subdev_8254_get_gate_src(dev, s, chan); 704 if (ret < 0) { 705 ret = -EINVAL; 706 break; 707 } 708 data[2] = ret; 709 break; 710 case INSN_CONFIG_SET_CLOCK_SRC: 711 ret = dio200_subdev_8254_set_clock_src(dev, s, chan, data[1]); 712 if (ret < 0) 713 ret = -EINVAL; 714 break; 715 case INSN_CONFIG_GET_CLOCK_SRC: 716 ret = dio200_subdev_8254_get_clock_src(dev, s, chan, &data[2]); 717 if (ret < 0) { 718 ret = -EINVAL; 719 break; 720 } 721 data[1] = ret; 722 break; 723 default: 724 ret = -EINVAL; 725 break; 726 } 727 spin_unlock_irqrestore(&subpriv->spinlock, flags); 728 return ret < 0 ? ret : insn->n; 729} 730 731static int dio200_subdev_8254_init(struct comedi_device *dev, 732 struct comedi_subdevice *s, 733 unsigned int offset) 734{ 735 const struct dio200_board *board = dev->board_ptr; 736 struct dio200_subdev_8254 *subpriv; 737 unsigned int chan; 738 739 subpriv = comedi_alloc_spriv(s, sizeof(*subpriv)); 740 if (!subpriv) 741 return -ENOMEM; 742 743 s->type = COMEDI_SUBD_COUNTER; 744 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 745 s->n_chan = 3; 746 s->maxdata = 0xFFFF; 747 s->insn_read = dio200_subdev_8254_read; 748 s->insn_write = dio200_subdev_8254_write; 749 s->insn_config = dio200_subdev_8254_config; 750 751 spin_lock_init(&subpriv->spinlock); 752 subpriv->ofs = offset; 753 if (board->has_clk_gat_sce) { 754 /* Derive CLK_SCE and GAT_SCE register offsets from 755 * 8254 offset. */ 756 subpriv->clk_sce_ofs = DIO200_XCLK_SCE + (offset >> 3); 757 subpriv->gat_sce_ofs = DIO200_XGAT_SCE + (offset >> 3); 758 subpriv->which = (offset >> 2) & 1; 759 } 760 761 /* Initialize channels. */ 762 for (chan = 0; chan < 3; chan++) { 763 dio200_subdev_8254_set_mode(dev, s, chan, 764 I8254_MODE0 | I8254_BINARY); 765 if (board->has_clk_gat_sce) { 766 /* Gate source 0 is VCC (logic 1). */ 767 dio200_subdev_8254_set_gate_src(dev, s, chan, 0); 768 /* Clock source 0 is the dedicated clock input. */ 769 dio200_subdev_8254_set_clock_src(dev, s, chan, 0); 770 } 771 } 772 773 return 0; 774} 775 776static void dio200_subdev_8255_set_dir(struct comedi_device *dev, 777 struct comedi_subdevice *s) 778{ 779 struct dio200_subdev_8255 *subpriv = s->private; 780 int config; 781 782 config = I8255_CTRL_CW; 783 /* 1 in io_bits indicates output, 1 in config indicates input */ 784 if (!(s->io_bits & 0x0000ff)) 785 config |= I8255_CTRL_A_IO; 786 if (!(s->io_bits & 0x00ff00)) 787 config |= I8255_CTRL_B_IO; 788 if (!(s->io_bits & 0x0f0000)) 789 config |= I8255_CTRL_C_LO_IO; 790 if (!(s->io_bits & 0xf00000)) 791 config |= I8255_CTRL_C_HI_IO; 792 dio200_write8(dev, subpriv->ofs + I8255_CTRL_REG, config); 793} 794 795static int dio200_subdev_8255_bits(struct comedi_device *dev, 796 struct comedi_subdevice *s, 797 struct comedi_insn *insn, 798 unsigned int *data) 799{ 800 struct dio200_subdev_8255 *subpriv = s->private; 801 unsigned int mask; 802 unsigned int val; 803 804 mask = comedi_dio_update_state(s, data); 805 if (mask) { 806 if (mask & 0xff) 807 dio200_write8(dev, subpriv->ofs + I8255_DATA_A_REG, 808 s->state & 0xff); 809 if (mask & 0xff00) 810 dio200_write8(dev, subpriv->ofs + I8255_DATA_B_REG, 811 (s->state >> 8) & 0xff); 812 if (mask & 0xff0000) 813 dio200_write8(dev, subpriv->ofs + I8255_DATA_C_REG, 814 (s->state >> 16) & 0xff); 815 } 816 817 val = dio200_read8(dev, subpriv->ofs + I8255_DATA_A_REG); 818 val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_B_REG) << 8; 819 val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_C_REG) << 16; 820 821 data[1] = val; 822 823 return insn->n; 824} 825 826static int dio200_subdev_8255_config(struct comedi_device *dev, 827 struct comedi_subdevice *s, 828 struct comedi_insn *insn, 829 unsigned int *data) 830{ 831 unsigned int chan = CR_CHAN(insn->chanspec); 832 unsigned int mask; 833 int ret; 834 835 if (chan < 8) 836 mask = 0x0000ff; 837 else if (chan < 16) 838 mask = 0x00ff00; 839 else if (chan < 20) 840 mask = 0x0f0000; 841 else 842 mask = 0xf00000; 843 844 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 845 if (ret) 846 return ret; 847 848 dio200_subdev_8255_set_dir(dev, s); 849 850 return insn->n; 851} 852 853static int dio200_subdev_8255_init(struct comedi_device *dev, 854 struct comedi_subdevice *s, 855 unsigned int offset) 856{ 857 struct dio200_subdev_8255 *subpriv; 858 859 subpriv = comedi_alloc_spriv(s, sizeof(*subpriv)); 860 if (!subpriv) 861 return -ENOMEM; 862 863 subpriv->ofs = offset; 864 865 s->type = COMEDI_SUBD_DIO; 866 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 867 s->n_chan = 24; 868 s->range_table = &range_digital; 869 s->maxdata = 1; 870 s->insn_bits = dio200_subdev_8255_bits; 871 s->insn_config = dio200_subdev_8255_config; 872 dio200_subdev_8255_set_dir(dev, s); 873 return 0; 874} 875 876static int dio200_subdev_timer_read(struct comedi_device *dev, 877 struct comedi_subdevice *s, 878 struct comedi_insn *insn, 879 unsigned int *data) 880{ 881 unsigned int n; 882 883 for (n = 0; n < insn->n; n++) 884 data[n] = dio200_read32(dev, DIO200_TS_COUNT); 885 return n; 886} 887 888static void dio200_subdev_timer_reset(struct comedi_device *dev, 889 struct comedi_subdevice *s) 890{ 891 unsigned int clock; 892 893 clock = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK; 894 dio200_write32(dev, DIO200_TS_CONFIG, clock | TS_CONFIG_RESET); 895 dio200_write32(dev, DIO200_TS_CONFIG, clock); 896} 897 898static void dio200_subdev_timer_get_clock_src(struct comedi_device *dev, 899 struct comedi_subdevice *s, 900 unsigned int *src, 901 unsigned int *period) 902{ 903 unsigned int clk; 904 905 clk = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK; 906 *src = clk; 907 *period = (clk < ARRAY_SIZE(ts_clock_period)) ? 908 ts_clock_period[clk] : 0; 909} 910 911static int dio200_subdev_timer_set_clock_src(struct comedi_device *dev, 912 struct comedi_subdevice *s, 913 unsigned int src) 914{ 915 if (src > TS_CONFIG_MAX_CLK_SRC) 916 return -EINVAL; 917 dio200_write32(dev, DIO200_TS_CONFIG, src); 918 return 0; 919} 920 921static int dio200_subdev_timer_config(struct comedi_device *dev, 922 struct comedi_subdevice *s, 923 struct comedi_insn *insn, 924 unsigned int *data) 925{ 926 int ret = 0; 927 928 switch (data[0]) { 929 case INSN_CONFIG_RESET: 930 dio200_subdev_timer_reset(dev, s); 931 break; 932 case INSN_CONFIG_SET_CLOCK_SRC: 933 ret = dio200_subdev_timer_set_clock_src(dev, s, data[1]); 934 if (ret < 0) 935 ret = -EINVAL; 936 break; 937 case INSN_CONFIG_GET_CLOCK_SRC: 938 dio200_subdev_timer_get_clock_src(dev, s, &data[1], &data[2]); 939 break; 940 default: 941 ret = -EINVAL; 942 break; 943 } 944 return ret < 0 ? ret : insn->n; 945} 946 947void amplc_dio200_set_enhance(struct comedi_device *dev, unsigned char val) 948{ 949 dio200_write8(dev, DIO200_ENHANCE, val); 950} 951EXPORT_SYMBOL_GPL(amplc_dio200_set_enhance); 952 953int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq, 954 unsigned long req_irq_flags) 955{ 956 const struct dio200_board *board = dev->board_ptr; 957 struct comedi_subdevice *s; 958 unsigned int n; 959 int ret; 960 961 ret = comedi_alloc_subdevices(dev, board->n_subdevs); 962 if (ret) 963 return ret; 964 965 for (n = 0; n < dev->n_subdevices; n++) { 966 s = &dev->subdevices[n]; 967 switch (board->sdtype[n]) { 968 case sd_8254: 969 /* counter subdevice (8254) */ 970 ret = dio200_subdev_8254_init(dev, s, 971 board->sdinfo[n]); 972 if (ret < 0) 973 return ret; 974 break; 975 case sd_8255: 976 /* digital i/o subdevice (8255) */ 977 ret = dio200_subdev_8255_init(dev, s, 978 board->sdinfo[n]); 979 if (ret < 0) 980 return ret; 981 break; 982 case sd_intr: 983 /* 'INTERRUPT' subdevice */ 984 if (irq && !dev->read_subdev) { 985 ret = dio200_subdev_intr_init(dev, s, 986 DIO200_INT_SCE, 987 board->sdinfo[n]); 988 if (ret < 0) 989 return ret; 990 dev->read_subdev = s; 991 } else { 992 s->type = COMEDI_SUBD_UNUSED; 993 } 994 break; 995 case sd_timer: 996 s->type = COMEDI_SUBD_TIMER; 997 s->subdev_flags = SDF_READABLE | SDF_LSAMPL; 998 s->n_chan = 1; 999 s->maxdata = 0xffffffff; 1000 s->insn_read = dio200_subdev_timer_read; 1001 s->insn_config = dio200_subdev_timer_config; 1002 break; 1003 default: 1004 s->type = COMEDI_SUBD_UNUSED; 1005 break; 1006 } 1007 } 1008 1009 if (irq && dev->read_subdev) { 1010 if (request_irq(irq, dio200_interrupt, req_irq_flags, 1011 dev->board_name, dev) >= 0) { 1012 dev->irq = irq; 1013 } else { 1014 dev_warn(dev->class_dev, 1015 "warning! irq %u unavailable!\n", irq); 1016 } 1017 } 1018 1019 return 0; 1020} 1021EXPORT_SYMBOL_GPL(amplc_dio200_common_attach); 1022 1023static int __init amplc_dio200_common_init(void) 1024{ 1025 return 0; 1026} 1027module_init(amplc_dio200_common_init); 1028 1029static void __exit amplc_dio200_common_exit(void) 1030{ 1031} 1032module_exit(amplc_dio200_common_exit); 1033 1034MODULE_AUTHOR("Comedi http://www.comedi.org"); 1035MODULE_DESCRIPTION("Comedi helper for amplc_dio200 and amplc_dio200_pci"); 1036MODULE_LICENSE("GPL"); 1037