1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * addi_apci_1564.c 4 * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. 5 * 6 * ADDI-DATA GmbH 7 * Dieselstrasse 3 8 * D-77833 Ottersweier 9 * Tel: +19(0)7223/9493-0 10 * Fax: +49(0)7223/9493-92 11 * http://www.addi-data.com 12 * info@addi-data.com 13 */ 14 15 /* 16 * Driver: addi_apci_1564 17 * Description: ADDI-DATA APCI-1564 Digital I/O board 18 * Devices: [ADDI-DATA] APCI-1564 (addi_apci_1564) 19 * Author: H Hartley Sweeten <hsweeten@visionengravers.com> 20 * Updated: Thu, 02 Jun 2016 13:12:46 -0700 21 * Status: untested 22 * 23 * Configuration Options: not applicable, uses comedi PCI auto config 24 * 25 * This board has the following features: 26 * - 32 optically isolated digital inputs (24V), 16 of which can 27 * generate change-of-state (COS) interrupts (channels 4 to 19) 28 * - 32 optically isolated digital outputs (10V to 36V) 29 * - 1 8-bit watchdog for resetting the outputs 30 * - 1 12-bit timer 31 * - 3 32-bit counters 32 * - 2 diagnostic inputs 33 * 34 * The COS, timer, and counter subdevices all use the dev->read_subdev to 35 * return the interrupt status. The sample data is updated and returned when 36 * any of these subdevices generate an interrupt. The sample data format is: 37 * 38 * Bit Description 39 * ----- ------------------------------------------ 40 * 31 COS interrupt 41 * 30 timer interrupt 42 * 29 counter 2 interrupt 43 * 28 counter 1 interrupt 44 * 27 counter 0 interrupt 45 * 26:20 not used 46 * 19:4 COS digital input state (channels 19 to 4) 47 * 3:0 not used 48 * 49 * The COS interrupts must be configured using an INSN_CONFIG_DIGITAL_TRIG 50 * instruction before they can be enabled by an async command. The COS 51 * interrupts will stay active until canceled. 52 * 53 * The timer subdevice does not use an async command. All control is handled 54 * by the (*insn_config). 55 * 56 * FIXME: The format of the ADDI_TCW_TIMEBASE_REG is not descibed in the 57 * datasheet I have. The INSN_CONFIG_SET_CLOCK_SRC currently just writes 58 * the raw data[1] to this register along with the raw data[2] value to the 59 * ADDI_TCW_RELOAD_REG. If anyone tests this and can determine the actual 60 * timebase/reload operation please let me know. 61 * 62 * The counter subdevice also does not use an async command. All control is 63 * handled by the (*insn_config). 64 * 65 * FIXME: The operation of the counters is not really described in the 66 * datasheet I have. The (*insn_config) needs more work. 67 */ 68 69 #include <linux/module.h> 70 #include <linux/interrupt.h> 71 #include <linux/comedi/comedi_pci.h> 72 73 #include "addi_tcw.h" 74 #include "addi_watchdog.h" 75 76 /* 77 * PCI BAR 0 78 * 79 * PLD Revision 1.0 I/O Mapping 80 * 0x00 93C76 EEPROM 81 * 0x04 - 0x18 Timer 12-Bit 82 * 83 * PLD Revision 2.x I/O Mapping 84 * 0x00 93C76 EEPROM 85 * 0x04 - 0x14 Digital Input 86 * 0x18 - 0x25 Digital Output 87 * 0x28 - 0x44 Watchdog 8-Bit 88 * 0x48 - 0x64 Timer 12-Bit 89 */ 90 #define APCI1564_EEPROM_REG 0x00 91 #define APCI1564_EEPROM_VCC_STATUS BIT(8) 92 #define APCI1564_EEPROM_TO_REV(x) (((x) >> 4) & 0xf) 93 #define APCI1564_EEPROM_DI BIT(3) 94 #define APCI1564_EEPROM_DO BIT(2) 95 #define APCI1564_EEPROM_CS BIT(1) 96 #define APCI1564_EEPROM_CLK BIT(0) 97 #define APCI1564_REV1_TIMER_IOBASE 0x04 98 #define APCI1564_REV2_MAIN_IOBASE 0x04 99 #define APCI1564_REV2_TIMER_IOBASE 0x48 100 101 /* 102 * PCI BAR 1 103 * 104 * PLD Revision 1.0 I/O Mapping 105 * 0x00 - 0x10 Digital Input 106 * 0x14 - 0x20 Digital Output 107 * 0x24 - 0x3c Watchdog 8-Bit 108 * 109 * PLD Revision 2.x I/O Mapping 110 * 0x00 Counter_0 111 * 0x20 Counter_1 112 * 0x30 Counter_3 113 */ 114 #define APCI1564_REV1_MAIN_IOBASE 0x00 115 116 /* 117 * dev->iobase Register Map 118 * PLD Revision 1.0 - PCI BAR 1 + 0x00 119 * PLD Revision 2.x - PCI BAR 0 + 0x04 120 */ 121 #define APCI1564_DI_REG 0x00 122 #define APCI1564_DI_INT_MODE1_REG 0x04 123 #define APCI1564_DI_INT_MODE2_REG 0x08 124 #define APCI1564_DI_INT_MODE_MASK 0x000ffff0 /* chans [19:4] */ 125 #define APCI1564_DI_INT_STATUS_REG 0x0c 126 #define APCI1564_DI_IRQ_REG 0x10 127 #define APCI1564_DI_IRQ_ENA BIT(2) 128 #define APCI1564_DI_IRQ_MODE BIT(1) /* 1=AND, 0=OR */ 129 #define APCI1564_DO_REG 0x14 130 #define APCI1564_DO_INT_CTRL_REG 0x18 131 #define APCI1564_DO_INT_CTRL_CC_INT_ENA BIT(1) 132 #define APCI1564_DO_INT_CTRL_VCC_INT_ENA BIT(0) 133 #define APCI1564_DO_INT_STATUS_REG 0x1c 134 #define APCI1564_DO_INT_STATUS_CC BIT(1) 135 #define APCI1564_DO_INT_STATUS_VCC BIT(0) 136 #define APCI1564_DO_IRQ_REG 0x20 137 #define APCI1564_DO_IRQ_INTR BIT(0) 138 #define APCI1564_WDOG_IOBASE 0x24 139 140 /* 141 * devpriv->timer Register Map (see addi_tcw.h for register/bit defines) 142 * PLD Revision 1.0 - PCI BAR 0 + 0x04 143 * PLD Revision 2.x - PCI BAR 0 + 0x48 144 */ 145 146 /* 147 * devpriv->counters Register Map (see addi_tcw.h for register/bit defines) 148 * PLD Revision 2.x - PCI BAR 1 + 0x00 149 */ 150 #define APCI1564_COUNTER(x) ((x) * 0x20) 151 152 /* 153 * The dev->read_subdev is used to return the interrupt events along with 154 * the state of the interrupt capable inputs. 155 */ 156 #define APCI1564_EVENT_COS BIT(31) 157 #define APCI1564_EVENT_TIMER BIT(30) 158 #define APCI1564_EVENT_COUNTER(x) BIT(27 + (x)) /* counter 0-2 */ 159 #define APCI1564_EVENT_MASK 0xfff0000f /* all but [19:4] */ 160 161 struct apci1564_private { 162 unsigned long eeprom; /* base address of EEPROM register */ 163 unsigned long timer; /* base address of 12-bit timer */ 164 unsigned long counters; /* base address of 32-bit counters */ 165 unsigned int mode1; /* rising-edge/high level channels */ 166 unsigned int mode2; /* falling-edge/low level channels */ 167 unsigned int ctrl; /* interrupt mode OR (edge) . AND (level) */ 168 }; 169 apci1564_reset(struct comedi_device * dev)170 static int apci1564_reset(struct comedi_device *dev) 171 { 172 struct apci1564_private *devpriv = dev->private; 173 174 /* Disable the input interrupts and reset status register */ 175 outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG); 176 inl(dev->iobase + APCI1564_DI_INT_STATUS_REG); 177 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG); 178 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG); 179 180 /* Reset the output channels and disable interrupts */ 181 outl(0x0, dev->iobase + APCI1564_DO_REG); 182 outl(0x0, dev->iobase + APCI1564_DO_INT_CTRL_REG); 183 184 /* Reset the watchdog registers */ 185 addi_watchdog_reset(dev->iobase + APCI1564_WDOG_IOBASE); 186 187 /* Reset the timer registers */ 188 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG); 189 outl(0x0, devpriv->timer + ADDI_TCW_RELOAD_REG); 190 191 if (devpriv->counters) { 192 unsigned long iobase = devpriv->counters + ADDI_TCW_CTRL_REG; 193 194 /* Reset the counter registers */ 195 outl(0x0, iobase + APCI1564_COUNTER(0)); 196 outl(0x0, iobase + APCI1564_COUNTER(1)); 197 outl(0x0, iobase + APCI1564_COUNTER(2)); 198 } 199 200 return 0; 201 } 202 apci1564_interrupt(int irq,void * d)203 static irqreturn_t apci1564_interrupt(int irq, void *d) 204 { 205 struct comedi_device *dev = d; 206 struct apci1564_private *devpriv = dev->private; 207 struct comedi_subdevice *s = dev->read_subdev; 208 unsigned int status; 209 unsigned int ctrl; 210 unsigned int chan; 211 212 s->state &= ~APCI1564_EVENT_MASK; 213 214 status = inl(dev->iobase + APCI1564_DI_IRQ_REG); 215 if (status & APCI1564_DI_IRQ_ENA) { 216 /* get the COS interrupt state and set the event flag */ 217 s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG); 218 s->state &= APCI1564_DI_INT_MODE_MASK; 219 s->state |= APCI1564_EVENT_COS; 220 221 /* clear the interrupt */ 222 outl(status & ~APCI1564_DI_IRQ_ENA, 223 dev->iobase + APCI1564_DI_IRQ_REG); 224 outl(status, dev->iobase + APCI1564_DI_IRQ_REG); 225 } 226 227 status = inl(devpriv->timer + ADDI_TCW_IRQ_REG); 228 if (status & ADDI_TCW_IRQ) { 229 s->state |= APCI1564_EVENT_TIMER; 230 231 /* clear the interrupt */ 232 ctrl = inl(devpriv->timer + ADDI_TCW_CTRL_REG); 233 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG); 234 outl(ctrl, devpriv->timer + ADDI_TCW_CTRL_REG); 235 } 236 237 if (devpriv->counters) { 238 for (chan = 0; chan < 3; chan++) { 239 unsigned long iobase; 240 241 iobase = devpriv->counters + APCI1564_COUNTER(chan); 242 243 status = inl(iobase + ADDI_TCW_IRQ_REG); 244 if (status & ADDI_TCW_IRQ) { 245 s->state |= APCI1564_EVENT_COUNTER(chan); 246 247 /* clear the interrupt */ 248 ctrl = inl(iobase + ADDI_TCW_CTRL_REG); 249 outl(0x0, iobase + ADDI_TCW_CTRL_REG); 250 outl(ctrl, iobase + ADDI_TCW_CTRL_REG); 251 } 252 } 253 } 254 255 if (s->state & APCI1564_EVENT_MASK) { 256 comedi_buf_write_samples(s, &s->state, 1); 257 comedi_handle_events(dev, s); 258 } 259 260 return IRQ_HANDLED; 261 } 262 apci1564_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)263 static int apci1564_di_insn_bits(struct comedi_device *dev, 264 struct comedi_subdevice *s, 265 struct comedi_insn *insn, 266 unsigned int *data) 267 { 268 data[1] = inl(dev->iobase + APCI1564_DI_REG); 269 270 return insn->n; 271 } 272 apci1564_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)273 static int apci1564_do_insn_bits(struct comedi_device *dev, 274 struct comedi_subdevice *s, 275 struct comedi_insn *insn, 276 unsigned int *data) 277 { 278 s->state = inl(dev->iobase + APCI1564_DO_REG); 279 280 if (comedi_dio_update_state(s, data)) 281 outl(s->state, dev->iobase + APCI1564_DO_REG); 282 283 data[1] = s->state; 284 285 return insn->n; 286 } 287 apci1564_diag_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)288 static int apci1564_diag_insn_bits(struct comedi_device *dev, 289 struct comedi_subdevice *s, 290 struct comedi_insn *insn, 291 unsigned int *data) 292 { 293 data[1] = inl(dev->iobase + APCI1564_DO_INT_STATUS_REG) & 3; 294 295 return insn->n; 296 } 297 298 /* 299 * Change-Of-State (COS) interrupt configuration 300 * 301 * Channels 4 to 19 are interruptible. These channels can be configured 302 * to generate interrupts based on AND/OR logic for the desired channels. 303 * 304 * OR logic 305 * - reacts to rising or falling edges 306 * - interrupt is generated when any enabled channel 307 * meet the desired interrupt condition 308 * 309 * AND logic 310 * - reacts to changes in level of the selected inputs 311 * - interrupt is generated when all enabled channels 312 * meet the desired interrupt condition 313 * - after an interrupt, a change in level must occur on 314 * the selected inputs to release the IRQ logic 315 * 316 * The COS interrupt must be configured before it can be enabled. 317 * 318 * data[0] : INSN_CONFIG_DIGITAL_TRIG 319 * data[1] : trigger number (= 0) 320 * data[2] : configuration operation: 321 * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts 322 * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts 323 * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts 324 * data[3] : left-shift for data[4] and data[5] 325 * data[4] : rising-edge/high level channels 326 * data[5] : falling-edge/low level channels 327 */ apci1564_cos_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)328 static int apci1564_cos_insn_config(struct comedi_device *dev, 329 struct comedi_subdevice *s, 330 struct comedi_insn *insn, 331 unsigned int *data) 332 { 333 struct apci1564_private *devpriv = dev->private; 334 unsigned int shift, oldmask, himask, lomask; 335 336 switch (data[0]) { 337 case INSN_CONFIG_DIGITAL_TRIG: 338 if (data[1] != 0) 339 return -EINVAL; 340 shift = data[3]; 341 if (shift < 32) { 342 oldmask = (1U << shift) - 1; 343 himask = data[4] << shift; 344 lomask = data[5] << shift; 345 } else { 346 oldmask = 0xffffffffu; 347 himask = 0; 348 lomask = 0; 349 } 350 switch (data[2]) { 351 case COMEDI_DIGITAL_TRIG_DISABLE: 352 devpriv->ctrl = 0; 353 devpriv->mode1 = 0; 354 devpriv->mode2 = 0; 355 outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG); 356 inl(dev->iobase + APCI1564_DI_INT_STATUS_REG); 357 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG); 358 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG); 359 break; 360 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES: 361 if (devpriv->ctrl != APCI1564_DI_IRQ_ENA) { 362 /* switching to 'OR' mode */ 363 devpriv->ctrl = APCI1564_DI_IRQ_ENA; 364 /* wipe old channels */ 365 devpriv->mode1 = 0; 366 devpriv->mode2 = 0; 367 } else { 368 /* preserve unspecified channels */ 369 devpriv->mode1 &= oldmask; 370 devpriv->mode2 &= oldmask; 371 } 372 /* configure specified channels */ 373 devpriv->mode1 |= himask; 374 devpriv->mode2 |= lomask; 375 break; 376 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS: 377 if (devpriv->ctrl != (APCI1564_DI_IRQ_ENA | 378 APCI1564_DI_IRQ_MODE)) { 379 /* switching to 'AND' mode */ 380 devpriv->ctrl = APCI1564_DI_IRQ_ENA | 381 APCI1564_DI_IRQ_MODE; 382 /* wipe old channels */ 383 devpriv->mode1 = 0; 384 devpriv->mode2 = 0; 385 } else { 386 /* preserve unspecified channels */ 387 devpriv->mode1 &= oldmask; 388 devpriv->mode2 &= oldmask; 389 } 390 /* configure specified channels */ 391 devpriv->mode1 |= himask; 392 devpriv->mode2 |= lomask; 393 break; 394 default: 395 return -EINVAL; 396 } 397 398 /* ensure the mode bits are in-range for channels [19:4] */ 399 devpriv->mode1 &= APCI1564_DI_INT_MODE_MASK; 400 devpriv->mode2 &= APCI1564_DI_INT_MODE_MASK; 401 break; 402 default: 403 return -EINVAL; 404 } 405 return insn->n; 406 } 407 apci1564_cos_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)408 static int apci1564_cos_insn_bits(struct comedi_device *dev, 409 struct comedi_subdevice *s, 410 struct comedi_insn *insn, 411 unsigned int *data) 412 { 413 data[1] = s->state; 414 415 return 0; 416 } 417 apci1564_cos_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)418 static int apci1564_cos_cmdtest(struct comedi_device *dev, 419 struct comedi_subdevice *s, 420 struct comedi_cmd *cmd) 421 { 422 int err = 0; 423 424 /* Step 1 : check if triggers are trivially valid */ 425 426 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); 427 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); 428 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW); 429 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 430 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE); 431 432 if (err) 433 return 1; 434 435 /* Step 2a : make sure trigger sources are unique */ 436 /* Step 2b : and mutually compatible */ 437 438 /* Step 3: check if arguments are trivially valid */ 439 440 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); 441 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 442 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); 443 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, 444 cmd->chanlist_len); 445 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); 446 447 if (err) 448 return 3; 449 450 /* Step 4: fix up any arguments */ 451 452 /* Step 5: check channel list if it exists */ 453 454 return 0; 455 } 456 457 /* 458 * Change-Of-State (COS) 'do_cmd' operation 459 * 460 * Enable the COS interrupt as configured by apci1564_cos_insn_config(). 461 */ apci1564_cos_cmd(struct comedi_device * dev,struct comedi_subdevice * s)462 static int apci1564_cos_cmd(struct comedi_device *dev, 463 struct comedi_subdevice *s) 464 { 465 struct apci1564_private *devpriv = dev->private; 466 467 if (!devpriv->ctrl && !(devpriv->mode1 || devpriv->mode2)) { 468 dev_warn(dev->class_dev, 469 "Interrupts disabled due to mode configuration!\n"); 470 return -EINVAL; 471 } 472 473 outl(devpriv->mode1, dev->iobase + APCI1564_DI_INT_MODE1_REG); 474 outl(devpriv->mode2, dev->iobase + APCI1564_DI_INT_MODE2_REG); 475 outl(devpriv->ctrl, dev->iobase + APCI1564_DI_IRQ_REG); 476 477 return 0; 478 } 479 apci1564_cos_cancel(struct comedi_device * dev,struct comedi_subdevice * s)480 static int apci1564_cos_cancel(struct comedi_device *dev, 481 struct comedi_subdevice *s) 482 { 483 outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG); 484 inl(dev->iobase + APCI1564_DI_INT_STATUS_REG); 485 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG); 486 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG); 487 488 return 0; 489 } 490 apci1564_timer_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)491 static int apci1564_timer_insn_config(struct comedi_device *dev, 492 struct comedi_subdevice *s, 493 struct comedi_insn *insn, 494 unsigned int *data) 495 { 496 struct apci1564_private *devpriv = dev->private; 497 unsigned int val; 498 499 switch (data[0]) { 500 case INSN_CONFIG_ARM: 501 if (data[1] > s->maxdata) 502 return -EINVAL; 503 outl(data[1], devpriv->timer + ADDI_TCW_RELOAD_REG); 504 outl(ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_TIMER_ENA, 505 devpriv->timer + ADDI_TCW_CTRL_REG); 506 break; 507 case INSN_CONFIG_DISARM: 508 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG); 509 break; 510 case INSN_CONFIG_GET_COUNTER_STATUS: 511 data[1] = 0; 512 val = inl(devpriv->timer + ADDI_TCW_CTRL_REG); 513 if (val & ADDI_TCW_CTRL_IRQ_ENA) 514 data[1] |= COMEDI_COUNTER_ARMED; 515 if (val & ADDI_TCW_CTRL_TIMER_ENA) 516 data[1] |= COMEDI_COUNTER_COUNTING; 517 val = inl(devpriv->timer + ADDI_TCW_STATUS_REG); 518 if (val & ADDI_TCW_STATUS_OVERFLOW) 519 data[1] |= COMEDI_COUNTER_TERMINAL_COUNT; 520 data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING | 521 COMEDI_COUNTER_TERMINAL_COUNT; 522 break; 523 case INSN_CONFIG_SET_CLOCK_SRC: 524 if (data[2] > s->maxdata) 525 return -EINVAL; 526 outl(data[1], devpriv->timer + ADDI_TCW_TIMEBASE_REG); 527 outl(data[2], devpriv->timer + ADDI_TCW_RELOAD_REG); 528 break; 529 case INSN_CONFIG_GET_CLOCK_SRC: 530 data[1] = inl(devpriv->timer + ADDI_TCW_TIMEBASE_REG); 531 data[2] = inl(devpriv->timer + ADDI_TCW_RELOAD_REG); 532 break; 533 default: 534 return -EINVAL; 535 } 536 537 return insn->n; 538 } 539 apci1564_timer_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)540 static int apci1564_timer_insn_write(struct comedi_device *dev, 541 struct comedi_subdevice *s, 542 struct comedi_insn *insn, 543 unsigned int *data) 544 { 545 struct apci1564_private *devpriv = dev->private; 546 547 /* just write the last to the reload register */ 548 if (insn->n) { 549 unsigned int val = data[insn->n - 1]; 550 551 outl(val, devpriv->timer + ADDI_TCW_RELOAD_REG); 552 } 553 554 return insn->n; 555 } 556 apci1564_timer_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)557 static int apci1564_timer_insn_read(struct comedi_device *dev, 558 struct comedi_subdevice *s, 559 struct comedi_insn *insn, 560 unsigned int *data) 561 { 562 struct apci1564_private *devpriv = dev->private; 563 int i; 564 565 /* return the actual value of the timer */ 566 for (i = 0; i < insn->n; i++) 567 data[i] = inl(devpriv->timer + ADDI_TCW_VAL_REG); 568 569 return insn->n; 570 } 571 apci1564_counter_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)572 static int apci1564_counter_insn_config(struct comedi_device *dev, 573 struct comedi_subdevice *s, 574 struct comedi_insn *insn, 575 unsigned int *data) 576 { 577 struct apci1564_private *devpriv = dev->private; 578 unsigned int chan = CR_CHAN(insn->chanspec); 579 unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan); 580 unsigned int val; 581 582 switch (data[0]) { 583 case INSN_CONFIG_ARM: 584 val = inl(iobase + ADDI_TCW_CTRL_REG); 585 val |= ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_CNTR_ENA; 586 outl(data[1], iobase + ADDI_TCW_RELOAD_REG); 587 outl(val, iobase + ADDI_TCW_CTRL_REG); 588 break; 589 case INSN_CONFIG_DISARM: 590 val = inl(iobase + ADDI_TCW_CTRL_REG); 591 val &= ~(ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_CNTR_ENA); 592 outl(val, iobase + ADDI_TCW_CTRL_REG); 593 break; 594 case INSN_CONFIG_SET_COUNTER_MODE: 595 /* 596 * FIXME: The counter operation is not described in the 597 * datasheet. For now just write the raw data[1] value to 598 * the control register. 599 */ 600 outl(data[1], iobase + ADDI_TCW_CTRL_REG); 601 break; 602 case INSN_CONFIG_GET_COUNTER_STATUS: 603 data[1] = 0; 604 val = inl(iobase + ADDI_TCW_CTRL_REG); 605 if (val & ADDI_TCW_CTRL_IRQ_ENA) 606 data[1] |= COMEDI_COUNTER_ARMED; 607 if (val & ADDI_TCW_CTRL_CNTR_ENA) 608 data[1] |= COMEDI_COUNTER_COUNTING; 609 val = inl(iobase + ADDI_TCW_STATUS_REG); 610 if (val & ADDI_TCW_STATUS_OVERFLOW) 611 data[1] |= COMEDI_COUNTER_TERMINAL_COUNT; 612 data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING | 613 COMEDI_COUNTER_TERMINAL_COUNT; 614 break; 615 default: 616 return -EINVAL; 617 } 618 619 return insn->n; 620 } 621 apci1564_counter_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)622 static int apci1564_counter_insn_write(struct comedi_device *dev, 623 struct comedi_subdevice *s, 624 struct comedi_insn *insn, 625 unsigned int *data) 626 { 627 struct apci1564_private *devpriv = dev->private; 628 unsigned int chan = CR_CHAN(insn->chanspec); 629 unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan); 630 631 /* just write the last to the reload register */ 632 if (insn->n) { 633 unsigned int val = data[insn->n - 1]; 634 635 outl(val, iobase + ADDI_TCW_RELOAD_REG); 636 } 637 638 return insn->n; 639 } 640 apci1564_counter_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)641 static int apci1564_counter_insn_read(struct comedi_device *dev, 642 struct comedi_subdevice *s, 643 struct comedi_insn *insn, 644 unsigned int *data) 645 { 646 struct apci1564_private *devpriv = dev->private; 647 unsigned int chan = CR_CHAN(insn->chanspec); 648 unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan); 649 int i; 650 651 /* return the actual value of the counter */ 652 for (i = 0; i < insn->n; i++) 653 data[i] = inl(iobase + ADDI_TCW_VAL_REG); 654 655 return insn->n; 656 } 657 apci1564_auto_attach(struct comedi_device * dev,unsigned long context_unused)658 static int apci1564_auto_attach(struct comedi_device *dev, 659 unsigned long context_unused) 660 { 661 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 662 struct apci1564_private *devpriv; 663 struct comedi_subdevice *s; 664 unsigned int val; 665 int ret; 666 667 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 668 if (!devpriv) 669 return -ENOMEM; 670 671 ret = comedi_pci_enable(dev); 672 if (ret) 673 return ret; 674 675 /* read the EEPROM register and check the I/O map revision */ 676 devpriv->eeprom = pci_resource_start(pcidev, 0); 677 val = inl(devpriv->eeprom + APCI1564_EEPROM_REG); 678 if (APCI1564_EEPROM_TO_REV(val) == 0) { 679 /* PLD Revision 1.0 I/O Mapping */ 680 dev->iobase = pci_resource_start(pcidev, 1) + 681 APCI1564_REV1_MAIN_IOBASE; 682 devpriv->timer = devpriv->eeprom + APCI1564_REV1_TIMER_IOBASE; 683 } else { 684 /* PLD Revision 2.x I/O Mapping */ 685 dev->iobase = devpriv->eeprom + APCI1564_REV2_MAIN_IOBASE; 686 devpriv->timer = devpriv->eeprom + APCI1564_REV2_TIMER_IOBASE; 687 devpriv->counters = pci_resource_start(pcidev, 1); 688 } 689 690 apci1564_reset(dev); 691 692 if (pcidev->irq > 0) { 693 ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED, 694 dev->board_name, dev); 695 if (ret == 0) 696 dev->irq = pcidev->irq; 697 } 698 699 ret = comedi_alloc_subdevices(dev, 7); 700 if (ret) 701 return ret; 702 703 /* Allocate and Initialise DI Subdevice Structures */ 704 s = &dev->subdevices[0]; 705 s->type = COMEDI_SUBD_DI; 706 s->subdev_flags = SDF_READABLE; 707 s->n_chan = 32; 708 s->maxdata = 1; 709 s->range_table = &range_digital; 710 s->insn_bits = apci1564_di_insn_bits; 711 712 /* Allocate and Initialise DO Subdevice Structures */ 713 s = &dev->subdevices[1]; 714 s->type = COMEDI_SUBD_DO; 715 s->subdev_flags = SDF_WRITABLE; 716 s->n_chan = 32; 717 s->maxdata = 1; 718 s->range_table = &range_digital; 719 s->insn_bits = apci1564_do_insn_bits; 720 721 /* Change-Of-State (COS) interrupt subdevice */ 722 s = &dev->subdevices[2]; 723 if (dev->irq) { 724 dev->read_subdev = s; 725 s->type = COMEDI_SUBD_DI; 726 s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_LSAMPL; 727 s->n_chan = 1; 728 s->maxdata = 1; 729 s->range_table = &range_digital; 730 s->len_chanlist = 1; 731 s->insn_config = apci1564_cos_insn_config; 732 s->insn_bits = apci1564_cos_insn_bits; 733 s->do_cmdtest = apci1564_cos_cmdtest; 734 s->do_cmd = apci1564_cos_cmd; 735 s->cancel = apci1564_cos_cancel; 736 } else { 737 s->type = COMEDI_SUBD_UNUSED; 738 } 739 740 /* Timer subdevice */ 741 s = &dev->subdevices[3]; 742 s->type = COMEDI_SUBD_TIMER; 743 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 744 s->n_chan = 1; 745 s->maxdata = 0x0fff; 746 s->range_table = &range_digital; 747 s->insn_config = apci1564_timer_insn_config; 748 s->insn_write = apci1564_timer_insn_write; 749 s->insn_read = apci1564_timer_insn_read; 750 751 /* Counter subdevice */ 752 s = &dev->subdevices[4]; 753 if (devpriv->counters) { 754 s->type = COMEDI_SUBD_COUNTER; 755 s->subdev_flags = SDF_WRITABLE | SDF_READABLE | SDF_LSAMPL; 756 s->n_chan = 3; 757 s->maxdata = 0xffffffff; 758 s->range_table = &range_digital; 759 s->insn_config = apci1564_counter_insn_config; 760 s->insn_write = apci1564_counter_insn_write; 761 s->insn_read = apci1564_counter_insn_read; 762 } else { 763 s->type = COMEDI_SUBD_UNUSED; 764 } 765 766 /* Initialize the watchdog subdevice */ 767 s = &dev->subdevices[5]; 768 ret = addi_watchdog_init(s, dev->iobase + APCI1564_WDOG_IOBASE); 769 if (ret) 770 return ret; 771 772 /* Initialize the diagnostic status subdevice */ 773 s = &dev->subdevices[6]; 774 s->type = COMEDI_SUBD_DI; 775 s->subdev_flags = SDF_READABLE; 776 s->n_chan = 2; 777 s->maxdata = 1; 778 s->range_table = &range_digital; 779 s->insn_bits = apci1564_diag_insn_bits; 780 781 return 0; 782 } 783 apci1564_detach(struct comedi_device * dev)784 static void apci1564_detach(struct comedi_device *dev) 785 { 786 if (dev->iobase) 787 apci1564_reset(dev); 788 comedi_pci_detach(dev); 789 } 790 791 static struct comedi_driver apci1564_driver = { 792 .driver_name = "addi_apci_1564", 793 .module = THIS_MODULE, 794 .auto_attach = apci1564_auto_attach, 795 .detach = apci1564_detach, 796 }; 797 apci1564_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)798 static int apci1564_pci_probe(struct pci_dev *dev, 799 const struct pci_device_id *id) 800 { 801 return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data); 802 } 803 804 static const struct pci_device_id apci1564_pci_table[] = { 805 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) }, 806 { 0 } 807 }; 808 MODULE_DEVICE_TABLE(pci, apci1564_pci_table); 809 810 static struct pci_driver apci1564_pci_driver = { 811 .name = "addi_apci_1564", 812 .id_table = apci1564_pci_table, 813 .probe = apci1564_pci_probe, 814 .remove = comedi_pci_auto_unconfig, 815 }; 816 module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver); 817 818 MODULE_AUTHOR("Comedi https://www.comedi.org"); 819 MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards"); 820 MODULE_LICENSE("GPL"); 821