1/* 2 * addi_apci_1564.c 3 * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. 4 * 5 * ADDI-DATA GmbH 6 * Dieselstrasse 3 7 * D-77833 Ottersweier 8 * Tel: +19(0)7223/9493-0 9 * Fax: +49(0)7223/9493-92 10 * http://www.addi-data.com 11 * info@addi-data.com 12 * 13 * This program is free software; you can redistribute it and/or modify it under 14 * the terms of the GNU General Public License as published by the Free Software 15 * Foundation; either version 2 of the License, or (at your option) any later 16 * version. 17 * 18 * This program is distributed in the hope that it will be useful, but WITHOUT 19 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 20 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 21 * details. 22 */ 23 24#include <linux/module.h> 25#include <linux/pci.h> 26#include <linux/interrupt.h> 27#include <linux/sched.h> 28 29#include "../comedidev.h" 30#include "comedi_fc.h" 31#include "amcc_s5933.h" 32#include "addi_watchdog.h" 33 34struct apci1564_private { 35 unsigned int amcc_iobase; /* base of AMCC I/O registers */ 36 unsigned int mode1; /* riding-edge/high level channels */ 37 unsigned int mode2; /* falling-edge/low level channels */ 38 unsigned int ctrl; /* interrupt mode OR (edge) . AND (level) */ 39 unsigned char timer_select_mode; 40 unsigned char mode_select_register; 41 struct task_struct *tsk_current; 42}; 43 44#include "addi-data/hwdrv_apci1564.c" 45 46static int apci1564_reset(struct comedi_device *dev) 47{ 48 struct apci1564_private *devpriv = dev->private; 49 50 /* Disable the input interrupts and reset status register */ 51 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG); 52 inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG); 53 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG); 54 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG); 55 56 /* Reset the output channels and disable interrupts */ 57 outl(0x0, devpriv->amcc_iobase + APCI1564_DO_REG); 58 outl(0x0, devpriv->amcc_iobase + APCI1564_DO_INT_CTRL_REG); 59 60 /* Reset the watchdog registers */ 61 addi_watchdog_reset(devpriv->amcc_iobase + APCI1564_WDOG_REG); 62 63 /* Reset the timer registers */ 64 outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG); 65 outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_RELOAD_REG); 66 67 /* Reset the counter registers */ 68 outl(0x0, dev->iobase + APCI1564_COUNTER_CTRL_REG(APCI1564_COUNTER1)); 69 outl(0x0, dev->iobase + APCI1564_COUNTER_CTRL_REG(APCI1564_COUNTER2)); 70 outl(0x0, dev->iobase + APCI1564_COUNTER_CTRL_REG(APCI1564_COUNTER3)); 71 outl(0x0, dev->iobase + APCI1564_COUNTER_CTRL_REG(APCI1564_COUNTER4)); 72 73 return 0; 74} 75 76static irqreturn_t apci1564_interrupt(int irq, void *d) 77{ 78 struct comedi_device *dev = d; 79 struct apci1564_private *devpriv = dev->private; 80 struct comedi_subdevice *s = dev->read_subdev; 81 unsigned int status; 82 unsigned int ctrl; 83 unsigned int chan; 84 85 /* check interrupt is from this device */ 86 if ((inl(devpriv->amcc_iobase + AMCC_OP_REG_INTCSR) & 87 INTCSR_INTR_ASSERTED) == 0) 88 return IRQ_NONE; 89 90 status = inl(devpriv->amcc_iobase + APCI1564_DI_IRQ_REG); 91 if (status & APCI1564_DI_INT_ENABLE) { 92 /* disable the interrupt */ 93 outl(status & APCI1564_DI_INT_DISABLE, 94 devpriv->amcc_iobase + APCI1564_DI_IRQ_REG); 95 96 s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG) 97 & 0xffff; 98 comedi_buf_put(s, s->state); 99 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; 100 comedi_event(dev, s); 101 102 /* enable the interrupt */ 103 outl(status, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG); 104 } 105 106 status = inl(devpriv->amcc_iobase + APCI1564_TIMER_IRQ_REG); 107 if (status & 0x01) { 108 /* Disable Timer Interrupt */ 109 ctrl = inl(devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG); 110 outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG); 111 112 /* Send a signal to from kernel to user space */ 113 send_sig(SIGIO, devpriv->tsk_current, 0); 114 115 /* Enable Timer Interrupt */ 116 outl(ctrl, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG); 117 } 118 119 for (chan = 0; chan < 4; chan++) { 120 status = inl(dev->iobase + APCI1564_COUNTER_IRQ_REG(chan)); 121 if (status & 0x01) { 122 /* Disable Counter Interrupt */ 123 ctrl = inl(dev->iobase + 124 APCI1564_COUNTER_CTRL_REG(chan)); 125 outl(0x0, dev->iobase + 126 APCI1564_COUNTER_CTRL_REG(chan)); 127 128 /* Send a signal to from kernel to user space */ 129 send_sig(SIGIO, devpriv->tsk_current, 0); 130 131 /* Enable Counter Interrupt */ 132 outl(ctrl, dev->iobase + 133 APCI1564_COUNTER_CTRL_REG(chan)); 134 } 135 } 136 137 return IRQ_HANDLED; 138} 139 140static int apci1564_di_insn_bits(struct comedi_device *dev, 141 struct comedi_subdevice *s, 142 struct comedi_insn *insn, 143 unsigned int *data) 144{ 145 struct apci1564_private *devpriv = dev->private; 146 147 data[1] = inl(devpriv->amcc_iobase + APCI1564_DI_REG); 148 149 return insn->n; 150} 151 152static int apci1564_do_insn_bits(struct comedi_device *dev, 153 struct comedi_subdevice *s, 154 struct comedi_insn *insn, 155 unsigned int *data) 156{ 157 struct apci1564_private *devpriv = dev->private; 158 159 s->state = inl(devpriv->amcc_iobase + APCI1564_DO_REG); 160 161 if (comedi_dio_update_state(s, data)) 162 outl(s->state, devpriv->amcc_iobase + APCI1564_DO_REG); 163 164 data[1] = s->state; 165 166 return insn->n; 167} 168 169static int apci1564_diag_insn_bits(struct comedi_device *dev, 170 struct comedi_subdevice *s, 171 struct comedi_insn *insn, 172 unsigned int *data) 173{ 174 struct apci1564_private *devpriv = dev->private; 175 176 data[1] = inl(devpriv->amcc_iobase + APCI1564_DO_INT_STATUS_REG) & 3; 177 178 return insn->n; 179} 180 181/* 182 * Change-Of-State (COS) interrupt configuration 183 * 184 * Channels 0 to 15 are interruptible. These channels can be configured 185 * to generate interrupts based on AND/OR logic for the desired channels. 186 * 187 * OR logic 188 * - reacts to rising or falling edges 189 * - interrupt is generated when any enabled channel 190 * meet the desired interrupt condition 191 * 192 * AND logic 193 * - reacts to changes in level of the selected inputs 194 * - interrupt is generated when all enabled channels 195 * meet the desired interrupt condition 196 * - after an interrupt, a change in level must occur on 197 * the selected inputs to release the IRQ logic 198 * 199 * The COS interrupt must be configured before it can be enabled. 200 * 201 * data[0] : INSN_CONFIG_DIGITAL_TRIG 202 * data[1] : trigger number (= 0) 203 * data[2] : configuration operation: 204 * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts 205 * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts 206 * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts 207 * data[3] : left-shift for data[4] and data[5] 208 * data[4] : rising-edge/high level channels 209 * data[5] : falling-edge/low level channels 210 */ 211static int apci1564_cos_insn_config(struct comedi_device *dev, 212 struct comedi_subdevice *s, 213 struct comedi_insn *insn, 214 unsigned int *data) 215{ 216 struct apci1564_private *devpriv = dev->private; 217 unsigned int shift, oldmask; 218 219 switch (data[0]) { 220 case INSN_CONFIG_DIGITAL_TRIG: 221 if (data[1] != 0) 222 return -EINVAL; 223 shift = data[3]; 224 oldmask = (1U << shift) - 1; 225 switch (data[2]) { 226 case COMEDI_DIGITAL_TRIG_DISABLE: 227 devpriv->ctrl = 0; 228 devpriv->mode1 = 0; 229 devpriv->mode2 = 0; 230 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG); 231 inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG); 232 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG); 233 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG); 234 break; 235 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES: 236 if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE | 237 APCI1564_DI_INT_OR)) { 238 /* switching to 'OR' mode */ 239 devpriv->ctrl = APCI1564_DI_INT_ENABLE | 240 APCI1564_DI_INT_OR; 241 /* wipe old channels */ 242 devpriv->mode1 = 0; 243 devpriv->mode2 = 0; 244 } else { 245 /* preserve unspecified channels */ 246 devpriv->mode1 &= oldmask; 247 devpriv->mode2 &= oldmask; 248 } 249 /* configure specified channels */ 250 devpriv->mode1 |= data[4] << shift; 251 devpriv->mode2 |= data[5] << shift; 252 break; 253 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS: 254 if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE | 255 APCI1564_DI_INT_AND)) { 256 /* switching to 'AND' mode */ 257 devpriv->ctrl = APCI1564_DI_INT_ENABLE | 258 APCI1564_DI_INT_AND; 259 /* wipe old channels */ 260 devpriv->mode1 = 0; 261 devpriv->mode2 = 0; 262 } else { 263 /* preserve unspecified channels */ 264 devpriv->mode1 &= oldmask; 265 devpriv->mode2 &= oldmask; 266 } 267 /* configure specified channels */ 268 devpriv->mode1 |= data[4] << shift; 269 devpriv->mode2 |= data[5] << shift; 270 break; 271 default: 272 return -EINVAL; 273 } 274 break; 275 default: 276 return -EINVAL; 277 } 278 return insn->n; 279} 280 281static int apci1564_cos_insn_bits(struct comedi_device *dev, 282 struct comedi_subdevice *s, 283 struct comedi_insn *insn, 284 unsigned int *data) 285{ 286 data[1] = s->state; 287 288 return 0; 289} 290 291static int apci1564_cos_cmdtest(struct comedi_device *dev, 292 struct comedi_subdevice *s, 293 struct comedi_cmd *cmd) 294{ 295 int err = 0; 296 297 /* Step 1 : check if triggers are trivially valid */ 298 299 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW); 300 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); 301 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW); 302 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 303 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE); 304 305 if (err) 306 return 1; 307 308 /* Step 2a : make sure trigger sources are unique */ 309 /* Step 2b : and mutually compatible */ 310 311 /* Step 3: check if arguments are trivially valid */ 312 313 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); 314 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 315 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0); 316 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); 317 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); 318 319 if (err) 320 return 3; 321 322 /* Step 4: fix up any arguments */ 323 324 /* Step 5: check channel list if it exists */ 325 326 return 0; 327} 328 329/* 330 * Change-Of-State (COS) 'do_cmd' operation 331 * 332 * Enable the COS interrupt as configured by apci1564_cos_insn_config(). 333 */ 334static int apci1564_cos_cmd(struct comedi_device *dev, 335 struct comedi_subdevice *s) 336{ 337 struct apci1564_private *devpriv = dev->private; 338 339 if (!devpriv->ctrl) { 340 dev_warn(dev->class_dev, 341 "Interrupts disabled due to mode configuration!\n"); 342 return -EINVAL; 343 } 344 345 outl(devpriv->mode1, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG); 346 outl(devpriv->mode2, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG); 347 outl(devpriv->ctrl, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG); 348 349 return 0; 350} 351 352static int apci1564_cos_cancel(struct comedi_device *dev, 353 struct comedi_subdevice *s) 354{ 355 struct apci1564_private *devpriv = dev->private; 356 357 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG); 358 inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG); 359 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG); 360 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG); 361 362 return 0; 363} 364 365static int apci1564_auto_attach(struct comedi_device *dev, 366 unsigned long context_unused) 367{ 368 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 369 struct apci1564_private *devpriv; 370 struct comedi_subdevice *s; 371 int ret; 372 373 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 374 if (!devpriv) 375 return -ENOMEM; 376 377 ret = comedi_pci_enable(dev); 378 if (ret) 379 return ret; 380 381 dev->iobase = pci_resource_start(pcidev, 1); 382 devpriv->amcc_iobase = pci_resource_start(pcidev, 0); 383 384 apci1564_reset(dev); 385 386 if (pcidev->irq > 0) { 387 ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED, 388 dev->board_name, dev); 389 if (ret == 0) 390 dev->irq = pcidev->irq; 391 } 392 393 ret = comedi_alloc_subdevices(dev, 6); 394 if (ret) 395 return ret; 396 397 /* Allocate and Initialise DI Subdevice Structures */ 398 s = &dev->subdevices[0]; 399 s->type = COMEDI_SUBD_DI; 400 s->subdev_flags = SDF_READABLE; 401 s->n_chan = 32; 402 s->maxdata = 1; 403 s->range_table = &range_digital; 404 s->insn_bits = apci1564_di_insn_bits; 405 406 /* Allocate and Initialise DO Subdevice Structures */ 407 s = &dev->subdevices[1]; 408 s->type = COMEDI_SUBD_DO; 409 s->subdev_flags = SDF_WRITEABLE; 410 s->n_chan = 32; 411 s->maxdata = 1; 412 s->range_table = &range_digital; 413 s->insn_bits = apci1564_do_insn_bits; 414 415 /* Change-Of-State (COS) interrupt subdevice */ 416 s = &dev->subdevices[2]; 417 if (dev->irq) { 418 dev->read_subdev = s; 419 s->type = COMEDI_SUBD_DI; 420 s->subdev_flags = SDF_READABLE | SDF_CMD_READ; 421 s->n_chan = 1; 422 s->maxdata = 1; 423 s->range_table = &range_digital; 424 s->len_chanlist = 1; 425 s->insn_config = apci1564_cos_insn_config; 426 s->insn_bits = apci1564_cos_insn_bits; 427 s->do_cmdtest = apci1564_cos_cmdtest; 428 s->do_cmd = apci1564_cos_cmd; 429 s->cancel = apci1564_cos_cancel; 430 } else { 431 s->type = COMEDI_SUBD_UNUSED; 432 } 433 434 /* Allocate and Initialise Timer Subdevice Structures */ 435 s = &dev->subdevices[3]; 436 s->type = COMEDI_SUBD_TIMER; 437 s->subdev_flags = SDF_WRITEABLE; 438 s->n_chan = 1; 439 s->maxdata = 0; 440 s->len_chanlist = 1; 441 s->range_table = &range_digital; 442 s->insn_write = apci1564_timer_write; 443 s->insn_read = apci1564_timer_read; 444 s->insn_config = apci1564_timer_config; 445 446 /* Initialize the watchdog subdevice */ 447 s = &dev->subdevices[4]; 448 ret = addi_watchdog_init(s, devpriv->amcc_iobase + APCI1564_WDOG_REG); 449 if (ret) 450 return ret; 451 452 /* Initialize the diagnostic status subdevice */ 453 s = &dev->subdevices[5]; 454 s->type = COMEDI_SUBD_DI; 455 s->subdev_flags = SDF_READABLE; 456 s->n_chan = 2; 457 s->maxdata = 1; 458 s->range_table = &range_digital; 459 s->insn_bits = apci1564_diag_insn_bits; 460 461 return 0; 462} 463 464static void apci1564_detach(struct comedi_device *dev) 465{ 466 if (dev->iobase) 467 apci1564_reset(dev); 468 comedi_pci_detach(dev); 469} 470 471static struct comedi_driver apci1564_driver = { 472 .driver_name = "addi_apci_1564", 473 .module = THIS_MODULE, 474 .auto_attach = apci1564_auto_attach, 475 .detach = apci1564_detach, 476}; 477 478static int apci1564_pci_probe(struct pci_dev *dev, 479 const struct pci_device_id *id) 480{ 481 return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data); 482} 483 484static const struct pci_device_id apci1564_pci_table[] = { 485 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) }, 486 { 0 } 487}; 488MODULE_DEVICE_TABLE(pci, apci1564_pci_table); 489 490static struct pci_driver apci1564_pci_driver = { 491 .name = "addi_apci_1564", 492 .id_table = apci1564_pci_table, 493 .probe = apci1564_pci_probe, 494 .remove = comedi_pci_auto_unconfig, 495}; 496module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver); 497 498MODULE_AUTHOR("Comedi http://www.comedi.org"); 499MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards"); 500MODULE_LICENSE("GPL"); 501