amplc_pc236.c revision 926848220e601338c918bdbde1d3fe7e63c8c04d
1/* 2 comedi/drivers/amplc_pc236.c 3 Driver for Amplicon PC36AT and PCI236 DIO boards. 4 5 Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/> 6 7 COMEDI - Linux Control and Measurement Device Interface 8 Copyright (C) 2000 David A. Schleef <ds@schleef.org> 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 2 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 24*/ 25/* 26Driver: amplc_pc236 27Description: Amplicon PC36AT, PCI236 28Author: Ian Abbott <abbotti@mev.co.uk> 29Devices: [Amplicon] PC36AT (pc36at), PCI236 (pci236 or amplc_pc236) 30Updated: Wed, 01 Apr 2009 15:41:25 +0100 31Status: works 32 33Configuration options - PC36AT: 34 [0] - I/O port base address 35 [1] - IRQ (optional) 36 37Configuration options - PCI236: 38 [0] - PCI bus of device (optional) 39 [1] - PCI slot of device (optional) 40 If bus/slot is not specified, the first available PCI device will be 41 used. 42 43The PC36AT ISA board and PCI236 PCI board have a single 8255 appearing 44as subdevice 0. 45 46Subdevice 1 pretends to be a digital input device, but it always returns 470 when read. However, if you run a command with scan_begin_src=TRIG_EXT, 48a rising edge on port C bit 3 acts as an external trigger, which can be 49used to wake up tasks. This is like the comedi_parport device, but the 50only way to physically disable the interrupt on the PC36AT is to remove 51the IRQ jumper. If no interrupt is connected, then subdevice 1 is 52unused. 53*/ 54 55#include <linux/pci.h> 56#include <linux/interrupt.h> 57 58#include "../comedidev.h" 59 60#include "comedi_fc.h" 61#include "8255.h" 62#include "plx9052.h" 63 64#define PC236_DRIVER_NAME "amplc_pc236" 65 66#define DO_ISA IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA) 67#define DO_PCI IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) 68 69/* PCI236 PCI configuration register information */ 70#define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009 71#define PCI_DEVICE_ID_INVALID 0xffff 72 73/* PC36AT / PCI236 registers */ 74 75#define PC236_IO_SIZE 4 76#define PC236_LCR_IO_SIZE 128 77 78/* Disable, and clear, interrupts */ 79#define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1POL | \ 80 PLX9052_INTCSR_LI2POL | \ 81 PLX9052_INTCSR_LI1SEL | \ 82 PLX9052_INTCSR_LI1CLRINT) 83 84/* Enable, and clear, interrupts */ 85#define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB | \ 86 PLX9052_INTCSR_LI1POL | \ 87 PLX9052_INTCSR_LI2POL | \ 88 PLX9052_INTCSR_PCIENAB | \ 89 PLX9052_INTCSR_LI1SEL | \ 90 PLX9052_INTCSR_LI1CLRINT) 91 92/* 93 * Board descriptions for Amplicon PC36AT and PCI236. 94 */ 95 96enum pc236_bustype { isa_bustype, pci_bustype }; 97enum pc236_model { pc36at_model, pci236_model, anypci_model }; 98 99struct pc236_board { 100 const char *name; 101 unsigned short devid; 102 enum pc236_bustype bustype; 103 enum pc236_model model; 104}; 105static const struct pc236_board pc236_boards[] = { 106#if DO_ISA 107 { 108 .name = "pc36at", 109 .bustype = isa_bustype, 110 .model = pc36at_model, 111 }, 112#endif 113#if DO_PCI 114 { 115 .name = "pci236", 116 .devid = PCI_DEVICE_ID_AMPLICON_PCI236, 117 .bustype = pci_bustype, 118 .model = pci236_model, 119 }, 120 { 121 .name = PC236_DRIVER_NAME, 122 .devid = PCI_DEVICE_ID_INVALID, 123 .bustype = pci_bustype, 124 .model = anypci_model, /* wildcard */ 125 }, 126#endif 127}; 128 129/* this structure is for data unique to this hardware driver. If 130 several hardware drivers keep similar information in this structure, 131 feel free to suggest moving the variable to the struct comedi_device struct. 132 */ 133struct pc236_private { 134 unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */ 135 int enable_irq; 136}; 137 138/* test if ISA supported and this is an ISA board */ 139static inline bool is_isa_board(const struct pc236_board *board) 140{ 141 return DO_ISA && board->bustype == isa_bustype; 142} 143 144/* test if PCI supported and this is a PCI board */ 145static inline bool is_pci_board(const struct pc236_board *board) 146{ 147 return DO_PCI && board->bustype == pci_bustype; 148} 149 150/* 151 * This function looks for a board matching the supplied PCI device. 152 */ 153static const struct pc236_board *pc236_find_pci_board(struct pci_dev *pci_dev) 154{ 155 unsigned int i; 156 157 for (i = 0; i < ARRAY_SIZE(pc236_boards); i++) 158 if (is_pci_board(&pc236_boards[i]) && 159 pci_dev->device == pc236_boards[i].devid) 160 return &pc236_boards[i]; 161 return NULL; 162} 163 164/* 165 * This function looks for a PCI device matching the requested board name, 166 * bus and slot. 167 */ 168static struct pci_dev *pc236_find_pci_dev(struct comedi_device *dev, 169 struct comedi_devconfig *it) 170{ 171 const struct pc236_board *thisboard = comedi_board(dev); 172 struct pci_dev *pci_dev = NULL; 173 int bus = it->options[0]; 174 int slot = it->options[1]; 175 176 for_each_pci_dev(pci_dev) { 177 if (bus || slot) { 178 if (bus != pci_dev->bus->number || 179 slot != PCI_SLOT(pci_dev->devfn)) 180 continue; 181 } 182 if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON) 183 continue; 184 185 if (thisboard->model == anypci_model) { 186 /* Wildcard board matches any supported PCI board. */ 187 const struct pc236_board *foundboard; 188 189 foundboard = pc236_find_pci_board(pci_dev); 190 if (foundboard == NULL) 191 continue; 192 /* Replace wildcard board_ptr. */ 193 dev->board_ptr = foundboard; 194 } else { 195 /* Match specific model name. */ 196 if (pci_dev->device != thisboard->devid) 197 continue; 198 } 199 return pci_dev; 200 } 201 dev_err(dev->class_dev, 202 "No supported board found! (req. bus %d, slot %d)\n", 203 bus, slot); 204 return NULL; 205} 206 207/* 208 * This function is called to mark the interrupt as disabled (no command 209 * configured on subdevice 1) and to physically disable the interrupt 210 * (not possible on the PC36AT, except by removing the IRQ jumper!). 211 */ 212static void pc236_intr_disable(struct comedi_device *dev) 213{ 214 const struct pc236_board *thisboard = comedi_board(dev); 215 struct pc236_private *devpriv = dev->private; 216 unsigned long flags; 217 218 spin_lock_irqsave(&dev->spinlock, flags); 219 devpriv->enable_irq = 0; 220 if (is_pci_board(thisboard)) 221 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR); 222 spin_unlock_irqrestore(&dev->spinlock, flags); 223} 224 225/* 226 * This function is called to mark the interrupt as enabled (a command 227 * configured on subdevice 1) and to physically enable the interrupt 228 * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!). 229 */ 230static void pc236_intr_enable(struct comedi_device *dev) 231{ 232 const struct pc236_board *thisboard = comedi_board(dev); 233 struct pc236_private *devpriv = dev->private; 234 unsigned long flags; 235 236 spin_lock_irqsave(&dev->spinlock, flags); 237 devpriv->enable_irq = 1; 238 if (is_pci_board(thisboard)) 239 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR); 240 spin_unlock_irqrestore(&dev->spinlock, flags); 241} 242 243/* 244 * This function is called when an interrupt occurs to check whether 245 * the interrupt has been marked as enabled and was generated by the 246 * board. If so, the function prepares the hardware for the next 247 * interrupt. 248 * Returns 0 if the interrupt should be ignored. 249 */ 250static int pc236_intr_check(struct comedi_device *dev) 251{ 252 const struct pc236_board *thisboard = comedi_board(dev); 253 struct pc236_private *devpriv = dev->private; 254 int retval = 0; 255 unsigned long flags; 256 unsigned int intcsr; 257 258 spin_lock_irqsave(&dev->spinlock, flags); 259 if (devpriv->enable_irq) { 260 retval = 1; 261 if (is_pci_board(thisboard)) { 262 intcsr = inl(devpriv->lcr_iobase + PLX9052_INTCSR); 263 if (!(intcsr & PLX9052_INTCSR_LI1STAT)) { 264 retval = 0; 265 } else { 266 /* Clear interrupt and keep it enabled. */ 267 outl(PCI236_INTR_ENABLE, 268 devpriv->lcr_iobase + PLX9052_INTCSR); 269 } 270 } 271 } 272 spin_unlock_irqrestore(&dev->spinlock, flags); 273 274 return retval; 275} 276 277/* 278 * Input from subdevice 1. 279 * Copied from the comedi_parport driver. 280 */ 281static int pc236_intr_insn(struct comedi_device *dev, 282 struct comedi_subdevice *s, struct comedi_insn *insn, 283 unsigned int *data) 284{ 285 data[1] = 0; 286 return insn->n; 287} 288 289/* 290 * Subdevice 1 command test. 291 * Copied from the comedi_parport driver. 292 */ 293static int pc236_intr_cmdtest(struct comedi_device *dev, 294 struct comedi_subdevice *s, 295 struct comedi_cmd *cmd) 296{ 297 int err = 0; 298 299 /* Step 1 : check if triggers are trivially valid */ 300 301 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW); 302 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); 303 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW); 304 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 305 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE); 306 307 if (err) 308 return 1; 309 310 /* Step 2a : make sure trigger sources are unique */ 311 /* Step 2b : and mutually compatible */ 312 313 if (err) 314 return 2; 315 316 /* Step 3: check it arguments are trivially valid */ 317 318 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); 319 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 320 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0); 321 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, 1); 322 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); 323 324 if (err) 325 return 3; 326 327 /* step 4: ignored */ 328 329 if (err) 330 return 4; 331 332 return 0; 333} 334 335/* 336 * Subdevice 1 command. 337 */ 338static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 339{ 340 pc236_intr_enable(dev); 341 342 return 0; 343} 344 345/* 346 * Subdevice 1 cancel command. 347 */ 348static int pc236_intr_cancel(struct comedi_device *dev, 349 struct comedi_subdevice *s) 350{ 351 pc236_intr_disable(dev); 352 353 return 0; 354} 355 356/* 357 * Interrupt service routine. 358 * Based on the comedi_parport driver. 359 */ 360static irqreturn_t pc236_interrupt(int irq, void *d) 361{ 362 struct comedi_device *dev = d; 363 struct comedi_subdevice *s = &dev->subdevices[1]; 364 int handled; 365 366 handled = pc236_intr_check(dev); 367 if (dev->attached && handled) { 368 comedi_buf_put(s->async, 0); 369 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; 370 comedi_event(dev, s); 371 } 372 return IRQ_RETVAL(handled); 373} 374 375static void pc236_report_attach(struct comedi_device *dev, unsigned int irq) 376{ 377 const struct pc236_board *thisboard = comedi_board(dev); 378 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 379 char tmpbuf[60]; 380 int tmplen; 381 382 if (is_isa_board(thisboard)) 383 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf), 384 "(base %#lx) ", dev->iobase); 385 else if (is_pci_board(thisboard)) 386 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf), 387 "(pci %s) ", pci_name(pcidev)); 388 else 389 tmplen = 0; 390 if (irq) 391 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen, 392 "(irq %u%s) ", irq, 393 (dev->irq ? "" : " UNAVAILABLE")); 394 else 395 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen, 396 "(no irq) "); 397 dev_info(dev->class_dev, "%s %sattached\n", 398 dev->board_name, tmpbuf); 399} 400 401static int pc236_common_attach(struct comedi_device *dev, unsigned long iobase, 402 unsigned int irq, unsigned long req_irq_flags) 403{ 404 const struct pc236_board *thisboard = comedi_board(dev); 405 struct comedi_subdevice *s; 406 int ret; 407 408 dev->board_name = thisboard->name; 409 dev->iobase = iobase; 410 411 ret = comedi_alloc_subdevices(dev, 2); 412 if (ret) 413 return ret; 414 415 s = &dev->subdevices[0]; 416 /* digital i/o subdevice (8255) */ 417 ret = subdev_8255_init(dev, s, NULL, iobase); 418 if (ret < 0) { 419 dev_err(dev->class_dev, "error! out of memory!\n"); 420 return ret; 421 } 422 s = &dev->subdevices[1]; 423 dev->read_subdev = s; 424 s->type = COMEDI_SUBD_UNUSED; 425 pc236_intr_disable(dev); 426 if (irq) { 427 if (request_irq(irq, pc236_interrupt, req_irq_flags, 428 PC236_DRIVER_NAME, dev) >= 0) { 429 dev->irq = irq; 430 s->type = COMEDI_SUBD_DI; 431 s->subdev_flags = SDF_READABLE | SDF_CMD_READ; 432 s->n_chan = 1; 433 s->maxdata = 1; 434 s->range_table = &range_digital; 435 s->insn_bits = pc236_intr_insn; 436 s->do_cmdtest = pc236_intr_cmdtest; 437 s->do_cmd = pc236_intr_cmd; 438 s->cancel = pc236_intr_cancel; 439 } 440 } 441 pc236_report_attach(dev, irq); 442 return 1; 443} 444 445static int pc236_pci_common_attach(struct comedi_device *dev, 446 struct pci_dev *pci_dev) 447{ 448 struct pc236_private *devpriv = dev->private; 449 unsigned long iobase; 450 int ret; 451 452 comedi_set_hw_dev(dev, &pci_dev->dev); 453 454 ret = comedi_pci_enable(dev); 455 if (ret) 456 return ret; 457 458 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1); 459 iobase = pci_resource_start(pci_dev, 2); 460 return pc236_common_attach(dev, iobase, pci_dev->irq, IRQF_SHARED); 461} 462 463/* 464 * Attach is called by the Comedi core to configure the driver 465 * for a particular board. If you specified a board_name array 466 * in the driver structure, dev->board_ptr contains that 467 * address. 468 */ 469static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it) 470{ 471 const struct pc236_board *thisboard = comedi_board(dev); 472 struct pc236_private *devpriv; 473 int ret; 474 475 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); 476 if (!devpriv) 477 return -ENOMEM; 478 dev->private = devpriv; 479 480 /* Process options according to bus type. */ 481 if (is_isa_board(thisboard)) { 482 ret = comedi_request_region(dev, it->options[0], PC236_IO_SIZE); 483 if (ret) 484 return ret; 485 486 return pc236_common_attach(dev, dev->iobase, it->options[1], 0); 487 } else if (is_pci_board(thisboard)) { 488 struct pci_dev *pci_dev; 489 490 pci_dev = pc236_find_pci_dev(dev, it); 491 if (!pci_dev) 492 return -EIO; 493 return pc236_pci_common_attach(dev, pci_dev); 494 } else { 495 dev_err(dev->class_dev, PC236_DRIVER_NAME 496 ": BUG! cannot determine board type!\n"); 497 return -EINVAL; 498 } 499} 500 501/* 502 * The auto_attach hook is called at PCI probe time via 503 * comedi_pci_auto_config(). dev->board_ptr is NULL on entry. 504 * There should be a board entry matching the supplied PCI device. 505 */ 506static int pc236_auto_attach(struct comedi_device *dev, 507 unsigned long context_unused) 508{ 509 struct pci_dev *pci_dev = comedi_to_pci_dev(dev); 510 struct pc236_private *devpriv; 511 512 if (!DO_PCI) 513 return -EINVAL; 514 515 dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach pci %s\n", 516 pci_name(pci_dev)); 517 518 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); 519 if (!devpriv) 520 return -ENOMEM; 521 dev->private = devpriv; 522 523 dev->board_ptr = pc236_find_pci_board(pci_dev); 524 if (dev->board_ptr == NULL) { 525 dev_err(dev->class_dev, "BUG! cannot determine board type!\n"); 526 return -EINVAL; 527 } 528 /* 529 * Need to 'get' the PCI device to match the 'put' in pc236_detach(). 530 * TODO: Remove the pci_dev_get() and matching pci_dev_put() once 531 * support for manual attachment of PCI devices via pc236_attach() 532 * has been removed. 533 */ 534 pci_dev_get(pci_dev); 535 return pc236_pci_common_attach(dev, pci_dev); 536} 537 538static void pc236_detach(struct comedi_device *dev) 539{ 540 const struct pc236_board *thisboard = comedi_board(dev); 541 542 if (!thisboard) 543 return; 544 if (dev->iobase) 545 pc236_intr_disable(dev); 546 if (dev->irq) 547 free_irq(dev->irq, dev); 548 if (dev->subdevices) 549 subdev_8255_cleanup(dev, &dev->subdevices[0]); 550 if (is_isa_board(thisboard)) { 551 if (dev->iobase) 552 release_region(dev->iobase, PC236_IO_SIZE); 553 } else if (is_pci_board(thisboard)) { 554 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 555 comedi_pci_disable(dev); 556 if (pcidev) 557 pci_dev_put(pcidev); 558 } 559} 560 561/* 562 * The struct comedi_driver structure tells the Comedi core module 563 * which functions to call to configure/deconfigure (attach/detach) 564 * the board, and also about the kernel module that contains 565 * the device code. 566 */ 567static struct comedi_driver amplc_pc236_driver = { 568 .driver_name = PC236_DRIVER_NAME, 569 .module = THIS_MODULE, 570 .attach = pc236_attach, 571 .auto_attach = pc236_auto_attach, 572 .detach = pc236_detach, 573 .board_name = &pc236_boards[0].name, 574 .offset = sizeof(struct pc236_board), 575 .num_names = ARRAY_SIZE(pc236_boards), 576}; 577 578#if DO_PCI 579static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = { 580 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236) }, 581 {0} 582}; 583 584MODULE_DEVICE_TABLE(pci, pc236_pci_table); 585 586static int amplc_pc236_pci_probe(struct pci_dev *dev, 587 const struct pci_device_id *id) 588{ 589 return comedi_pci_auto_config(dev, &lc_pc236_driver, 590 id->driver_data); 591} 592 593static struct pci_driver amplc_pc236_pci_driver = { 594 .name = PC236_DRIVER_NAME, 595 .id_table = pc236_pci_table, 596 .probe = &lc_pc236_pci_probe, 597 .remove = comedi_pci_auto_unconfig, 598}; 599 600module_comedi_pci_driver(amplc_pc236_driver, amplc_pc236_pci_driver); 601#else 602module_comedi_driver(amplc_pc236_driver); 603#endif 604 605MODULE_AUTHOR("Comedi http://www.comedi.org"); 606MODULE_DESCRIPTION("Comedi low-level driver"); 607MODULE_LICENSE("GPL"); 608