pcmuio.c revision 790c55415aa31f4c732729f94d2c3a54f7d3bfc2
1/* 2 comedi/drivers/pcmuio.c 3 Driver for Winsystems PC-104 based 48-channel and 96-channel DIO boards. 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org> 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21*/ 22/* 23Driver: pcmuio 24Description: A driver for the PCM-UIO48A and PCM-UIO96A boards from Winsystems. 25Devices: [Winsystems] PCM-UIO48A (pcmuio48), PCM-UIO96A (pcmuio96) 26Author: Calin Culianu <calin@ajvar.org> 27Updated: Fri, 13 Jan 2006 12:01:01 -0500 28Status: works 29 30A driver for the relatively straightforward-to-program PCM-UIO48A and 31PCM-UIO96A boards from Winsystems. These boards use either one or two 32(in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO). 33This chip is interesting in that each I/O line is individually 34programmable for INPUT or OUTPUT (thus comedi_dio_config can be done 35on a per-channel basis). Also, each chip supports edge-triggered 36interrupts for the first 24 I/O lines. Of course, since the 3796-channel version of the board has two ASICs, it can detect polarity 38changes on up to 48 I/O lines. Since this is essentially an (non-PnP) 39ISA board, I/O Address and IRQ selection are done through jumpers on 40the board. You need to pass that information to this driver as the 41first and second comedi_config option, respectively. Note that the 4248-channel version uses 16 bytes of IO memory and the 96-channel 43version uses 32-bytes (in case you are worried about conflicts). The 4448-channel board is split into two 24-channel comedi subdevices. 45The 96-channel board is split into 4 24-channel DIO subdevices. 46 47Note that IRQ support has been added, but it is untested. 48 49To use edge-detection IRQ support, pass the IRQs of both ASICS 50(for the 96 channel version) or just 1 ASIC (for 48-channel version). 51Then, use use comedi_commands with TRIG_NOW. 52Your callback will be called each time an edge is triggered, and the data 53values will be two sample_t's, which should be concatenated to form one 5432-bit unsigned int. This value is the mask of channels that had 55edges detected from your channel list. Note that the bits positions 56in the mask correspond to positions in your chanlist when you specified 57the command and *not* channel id's! 58 59To set the polarity of the edge-detection interrupts pass a nonzero value for 60either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for both 61CR_RANGE and CR_AREF if you want edge-down polarity. 62 63In the 48-channel version: 64 65On subdev 0, the first 24 channels channels are edge-detect channels. 66 67In the 96-channel board you have the collowing channels that can do edge detection: 68 69subdev 0, channels 0-24 (first 24 channels of 1st ASIC) 70subdev 2, channels 0-24 (first 24 channels of 2nd ASIC) 71 72Configuration Options: 73 [0] - I/O port base address 74 [1] - IRQ (for first ASIC, or first 24 channels) 75 [2] - IRQ for second ASIC (pcmuio96 only - IRQ for chans 48-72 .. can be the same as first irq!) 76*/ 77 78#include "../comedidev.h" 79 80#include <linux/pci.h> /* for PCI devices */ 81 82#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) 83#define CHANS_PER_PORT 8 84#define PORTS_PER_ASIC 6 85#define INTR_PORTS_PER_ASIC 3 86#define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */ 87#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT) 88#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC) 89#define INTR_CHANS_PER_ASIC 24 90#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT) 91#define MAX_DIO_CHANS (PORTS_PER_ASIC*2*CHANS_PER_PORT) 92#define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC) 93#define SDEV_NO ((int)(s - dev->subdevices)) 94#define CALC_N_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/) 95/* IO Memory sizes */ 96#define ASIC_IOSIZE (0x10) 97#define PCMUIO48_IOSIZE ASIC_IOSIZE 98#define PCMUIO96_IOSIZE (ASIC_IOSIZE*2) 99 100/* Some offsets - these are all in the 16byte IO memory offset from 101 the base address. Note that there is a paging scheme to swap out 102 offsets 0x8-0xA using the PAGELOCK register. See the table below. 103 104 Register(s) Pages R/W? Description 105 -------------------------------------------------------------- 106 REG_PORTx All R/W Read/Write/Configure IO 107 REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int. 108 REG_PAGELOCK All WriteOnly Select a page 109 REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity 110 REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int. 111 REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints. 112 */ 113#define REG_PORT0 0x0 114#define REG_PORT1 0x1 115#define REG_PORT2 0x2 116#define REG_PORT3 0x3 117#define REG_PORT4 0x4 118#define REG_PORT5 0x5 119#define REG_INT_PENDING 0x6 120#define REG_PAGELOCK 0x7 /* page selector register, upper 2 bits select a page 121 and bits 0-5 are used to 'lock down' a particular 122 port above to make it readonly. */ 123#define REG_POL0 0x8 124#define REG_POL1 0x9 125#define REG_POL2 0xA 126#define REG_ENAB0 0x8 127#define REG_ENAB1 0x9 128#define REG_ENAB2 0xA 129#define REG_INT_ID0 0x8 130#define REG_INT_ID1 0x9 131#define REG_INT_ID2 0xA 132 133#define NUM_PAGED_REGS 3 134#define NUM_PAGES 4 135#define FIRST_PAGED_REG 0x8 136#define REG_PAGE_BITOFFSET 6 137#define REG_LOCK_BITOFFSET 0 138#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1)) 139#define REG_LOCK_MASK ~(REG_PAGE_MASK) 140#define PAGE_POL 1 141#define PAGE_ENAB 2 142#define PAGE_INT_ID 3 143 144/* 145 * Board descriptions for two imaginary boards. Describing the 146 * boards in this way is optional, and completely driver-dependent. 147 * Some drivers use arrays such as this, other do not. 148 */ 149typedef struct pcmuio_board_struct { 150 const char *name; 151 const int num_asics; 152 const int num_channels_per_port; 153 const int num_ports; 154} pcmuio_board; 155 156static const pcmuio_board pcmuio_boards[] = { 157 { 158 name: "pcmuio48", 159 num_asics:1, 160 num_ports:6, 161 }, 162 { 163 name: "pcmuio96", 164 num_asics:2, 165 num_ports:12, 166 }, 167}; 168 169/* 170 * Useful for shorthand access to the particular board structure 171 */ 172#define thisboard ((const pcmuio_board *)dev->board_ptr) 173 174/* this structure is for data unique to this subdevice. */ 175typedef struct { 176 /* mapping of halfwords (bytes) in port/chanarray to iobase */ 177 unsigned long iobases[PORTS_PER_SUBDEV]; 178 179 /* The below is only used for intr subdevices */ 180 struct { 181 int asic; /* if non-negative, this subdev has an interrupt asic */ 182 int first_chan; /* if nonnegative, the first channel id for 183 interrupts. */ 184 int num_asic_chans; /* the number of asic channels in this subdev 185 that have interrutps */ 186 int asic_chan; /* if nonnegative, the first channel id with 187 respect to the asic that has interrupts */ 188 int enabled_mask; /* subdev-relative channel mask for channels 189 we are interested in */ 190 int active; 191 int stop_count; 192 int continuous; 193 spinlock_t spinlock; 194 } intr; 195} pcmuio_subdev_private; 196 197/* this structure is for data unique to this hardware driver. If 198 several hardware drivers keep similar information in this structure, 199 feel free to suggest moving the variable to the comedi_device struct. */ 200typedef struct { 201 struct { 202 unsigned char pagelock; /* current page and lock */ 203 unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */ 204 unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */ 205 int num; 206 unsigned long iobase; 207 unsigned int irq; 208 spinlock_t spinlock; 209 } asics[MAX_ASICS]; 210 pcmuio_subdev_private *sprivs; 211} pcmuio_private; 212 213/* 214 * most drivers define the following macro to make it easy to 215 * access the private structure. 216 */ 217#define devpriv ((pcmuio_private *)dev->private) 218#define subpriv ((pcmuio_subdev_private *)s->private) 219/* 220 * The comedi_driver structure tells the Comedi core module 221 * which functions to call to configure/deconfigure (attach/detach) 222 * the board, and also about the kernel module that contains 223 * the device code. 224 */ 225static int pcmuio_attach(comedi_device * dev, comedi_devconfig * it); 226static int pcmuio_detach(comedi_device * dev); 227 228static comedi_driver driver = { 229 driver_name:"pcmuio", 230 module:THIS_MODULE, 231 attach:pcmuio_attach, 232 detach:pcmuio_detach, 233/* It is not necessary to implement the following members if you are 234 * writing a driver for a ISA PnP or PCI card */ 235 /* Most drivers will support multiple types of boards by 236 * having an array of board structures. These were defined 237 * in pcmuio_boards[] above. Note that the element 'name' 238 * was first in the structure -- Comedi uses this fact to 239 * extract the name of the board without knowing any details 240 * about the structure except for its length. 241 * When a device is attached (by comedi_config), the name 242 * of the device is given to Comedi, and Comedi tries to 243 * match it by going through the list of board names. If 244 * there is a match, the address of the pointer is put 245 * into dev->board_ptr and driver->attach() is called. 246 * 247 * Note that these are not necessary if you can determine 248 * the type of board in software. ISA PnP, PCI, and PCMCIA 249 * devices are such boards. 250 */ 251 board_name:&pcmuio_boards[0].name, 252 offset:sizeof(pcmuio_board), 253 num_names:sizeof(pcmuio_boards) / sizeof(pcmuio_board), 254}; 255 256static int pcmuio_dio_insn_bits(comedi_device * dev, comedi_subdevice * s, 257 comedi_insn * insn, unsigned int * data); 258static int pcmuio_dio_insn_config(comedi_device * dev, comedi_subdevice * s, 259 comedi_insn * insn, unsigned int * data); 260 261static irqreturn_t interrupt_pcmuio(int irq, void *d PT_REGS_ARG); 262static void pcmuio_stop_intr(comedi_device *, comedi_subdevice *); 263static int pcmuio_cancel(comedi_device * dev, comedi_subdevice * s); 264static int pcmuio_cmd(comedi_device * dev, comedi_subdevice * s); 265static int pcmuio_cmdtest(comedi_device * dev, comedi_subdevice * s, 266 comedi_cmd * cmd); 267 268/* some helper functions to deal with specifics of this device's registers */ 269static void init_asics(comedi_device * dev); /* sets up/clears ASIC chips to defaults */ 270static void switch_page(comedi_device * dev, int asic, int page); 271#ifdef notused 272static void lock_port(comedi_device * dev, int asic, int port); 273static void unlock_port(comedi_device * dev, int asic, int port); 274#endif 275 276/* 277 * Attach is called by the Comedi core to configure the driver 278 * for a particular board. If you specified a board_name array 279 * in the driver structure, dev->board_ptr contains that 280 * address. 281 */ 282static int pcmuio_attach(comedi_device * dev, comedi_devconfig * it) 283{ 284 comedi_subdevice *s; 285 int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0; 286 unsigned long iobase; 287 unsigned int irq[MAX_ASICS]; 288 289 iobase = it->options[0]; 290 irq[0] = it->options[1]; 291 irq[1] = it->options[2]; 292 293 printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name, 294 iobase); 295 296 dev->iobase = iobase; 297 298 if (!iobase || !request_region(iobase, 299 thisboard->num_asics * ASIC_IOSIZE, 300 driver.driver_name)) { 301 printk("I/O port conflict\n"); 302 return -EIO; 303 } 304 305/* 306 * Initialize dev->board_name. Note that we can use the "thisboard" 307 * macro now, since we just initialized it in the last line. 308 */ 309 dev->board_name = thisboard->name; 310 311/* 312 * Allocate the private structure area. alloc_private() is a 313 * convenient macro defined in comedidev.h. 314 */ 315 if (alloc_private(dev, sizeof(pcmuio_private)) < 0) { 316 printk("cannot allocate private data structure\n"); 317 return -ENOMEM; 318 } 319 320 for (asic = 0; asic < MAX_ASICS; ++asic) { 321 devpriv->asics[asic].num = asic; 322 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE; 323 devpriv->asics[asic].irq = 0; /* this gets actually set at the end of 324 this function when we 325 comedi_request_irqs */ 326 spin_lock_init(&devpriv->asics[asic].spinlock); 327 } 328 329 chans_left = CHANS_PER_ASIC * thisboard->num_asics; 330 n_subdevs = CALC_N_SUBDEVS(chans_left); 331 devpriv->sprivs = 332 kcalloc(n_subdevs, sizeof(pcmuio_subdev_private), GFP_KERNEL); 333 if (!devpriv->sprivs) { 334 printk("cannot allocate subdevice private data structures\n"); 335 return -ENOMEM; 336 } 337 /* 338 * Allocate the subdevice structures. alloc_subdevice() is a 339 * convenient macro defined in comedidev.h. 340 * 341 * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the 342 * 96-channel version of the board. 343 */ 344 if (alloc_subdevices(dev, n_subdevs) < 0) { 345 printk("cannot allocate subdevice data structures\n"); 346 return -ENOMEM; 347 } 348 349 port = 0; 350 asic = 0; 351 for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) { 352 int byte_no; 353 354 s = dev->subdevices + sdev_no; 355 s->private = devpriv->sprivs + sdev_no; 356 s->maxdata = 1; 357 s->range_table = &range_digital; 358 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 359 s->type = COMEDI_SUBD_DIO; 360 s->insn_bits = pcmuio_dio_insn_bits; 361 s->insn_config = pcmuio_dio_insn_config; 362 s->n_chan = MIN(chans_left, MAX_CHANS_PER_SUBDEV); 363 subpriv->intr.asic = -1; 364 subpriv->intr.first_chan = -1; 365 subpriv->intr.asic_chan = -1; 366 subpriv->intr.num_asic_chans = -1; 367 subpriv->intr.active = 0; 368 s->len_chanlist = 1; 369 370 /* save the ioport address for each 'port' of 8 channels in the 371 subdevice */ 372 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) { 373 if (port >= PORTS_PER_ASIC) { 374 port = 0; 375 ++asic; 376 thisasic_chanct = 0; 377 } 378 subpriv->iobases[byte_no] = 379 devpriv->asics[asic].iobase + port; 380 381 if (thisasic_chanct < 382 CHANS_PER_PORT * INTR_PORTS_PER_ASIC 383 && subpriv->intr.asic < 0) { 384 /* this is an interrupt subdevice, so setup the struct */ 385 subpriv->intr.asic = asic; 386 subpriv->intr.active = 0; 387 subpriv->intr.stop_count = 0; 388 subpriv->intr.first_chan = byte_no * 8; 389 subpriv->intr.asic_chan = thisasic_chanct; 390 subpriv->intr.num_asic_chans = 391 s->n_chan - subpriv->intr.first_chan; 392 dev->read_subdev = s; 393 s->subdev_flags |= SDF_CMD_READ; 394 s->cancel = pcmuio_cancel; 395 s->do_cmd = pcmuio_cmd; 396 s->do_cmdtest = pcmuio_cmdtest; 397 s->len_chanlist = subpriv->intr.num_asic_chans; 398 } 399 thisasic_chanct += CHANS_PER_PORT; 400 } 401 spin_lock_init(&subpriv->intr.spinlock); 402 403 chans_left -= s->n_chan; 404 405 if (!chans_left) { 406 asic = 0; /* reset the asic to our first asic, to do intr subdevs */ 407 port = 0; 408 } 409 410 } 411 412 init_asics(dev); /* clear out all the registers, basically */ 413 414 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) { 415 if (irq[asic] 416 && comedi_request_irq(irq[asic], interrupt_pcmuio, 417 IRQF_SHARED, thisboard->name, dev)) { 418 int i; 419 /* unroll the allocated irqs.. */ 420 for (i = asic - 1; i >= 0; --i) { 421 comedi_free_irq(irq[i], dev); 422 devpriv->asics[i].irq = irq[i] = 0; 423 } 424 irq[asic] = 0; 425 } 426 devpriv->asics[asic].irq = irq[asic]; 427 } 428 429 dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple 430 irqs.. */ 431 432 if (irq[0]) { 433 printk("irq: %u ", irq[0]); 434 if (irq[1] && thisboard->num_asics == 2) 435 printk("second ASIC irq: %u ", irq[1]); 436 } else { 437 printk("(IRQ mode disabled) "); 438 } 439 440 printk("attached\n"); 441 442 return 1; 443} 444 445/* 446 * _detach is called to deconfigure a device. It should deallocate 447 * resources. 448 * This function is also called when _attach() fails, so it should be 449 * careful not to release resources that were not necessarily 450 * allocated by _attach(). dev->private and dev->subdevices are 451 * deallocated automatically by the core. 452 */ 453static int pcmuio_detach(comedi_device * dev) 454{ 455 int i; 456 457 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name); 458 if (dev->iobase) 459 release_region(dev->iobase, ASIC_IOSIZE * thisboard->num_asics); 460 461 for (i = 0; i < MAX_ASICS; ++i) { 462 if (devpriv->asics[i].irq) 463 comedi_free_irq(devpriv->asics[i].irq, dev); 464 } 465 466 if (devpriv && devpriv->sprivs) 467 kfree(devpriv->sprivs); 468 469 return 0; 470} 471 472/* DIO devices are slightly special. Although it is possible to 473 * implement the insn_read/insn_write interface, it is much more 474 * useful to applications if you implement the insn_bits interface. 475 * This allows packed reading/writing of the DIO channels. The 476 * comedi core can convert between insn_bits and insn_read/write */ 477static int pcmuio_dio_insn_bits(comedi_device * dev, comedi_subdevice * s, 478 comedi_insn * insn, unsigned int * data) 479{ 480 int byte_no; 481 if (insn->n != 2) 482 return -EINVAL; 483 484 /* NOTE: 485 reading a 0 means this channel was high 486 writine a 0 sets the channel high 487 reading a 1 means this channel was low 488 writing a 1 means set this channel low 489 490 Therefore everything is always inverted. */ 491 492 /* The insn data is a mask in data[0] and the new data 493 * in data[1], each channel cooresponding to a bit. */ 494 495#ifdef DAMMIT_ITS_BROKEN 496 /* DEBUG */ 497 printk("write mask: %08x data: %08x\n", data[0], data[1]); 498#endif 499 500 s->state = 0; 501 502 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) { 503 /* address of 8-bit port */ 504 unsigned long ioaddr = subpriv->iobases[byte_no], 505 /* bit offset of port in 32-bit doubleword */ 506 offset = byte_no * 8; 507 /* this 8-bit port's data */ 508 unsigned char byte = 0, 509 /* The write mask for this port (if any) */ 510 write_mask_byte = (data[0] >> offset) & 0xff, 511 /* The data byte for this port */ 512 data_byte = (data[1] >> offset) & 0xff; 513 514 byte = inb(ioaddr); /* read all 8-bits for this port */ 515 516#ifdef DAMMIT_ITS_BROKEN 517 /* DEBUG */ 518 printk("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ", byte_no, (unsigned)write_mask_byte, (unsigned)data_byte, offset, ioaddr, (unsigned)byte); 519#endif 520 521 if (write_mask_byte) { 522 /* this byte has some write_bits -- so set the output lines */ 523 byte &= ~write_mask_byte; /* clear bits for write mask */ 524 byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */ 525 /* Write out the new digital output state */ 526 outb(byte, ioaddr); 527 } 528#ifdef DAMMIT_ITS_BROKEN 529 /* DEBUG */ 530 printk("data_out_byte %02x\n", (unsigned)byte); 531#endif 532 /* save the digital input lines for this byte.. */ 533 s->state |= ((unsigned int)byte) << offset; 534 } 535 536 /* now return the DIO lines to data[1] - note they came inverted! */ 537 data[1] = ~s->state; 538 539#ifdef DAMMIT_ITS_BROKEN 540 /* DEBUG */ 541 printk("s->state %08x data_out %08x\n", s->state, data[1]); 542#endif 543 544 return 2; 545} 546 547/* The input or output configuration of each digital line is 548 * configured by a special insn_config instruction. chanspec 549 * contains the channel to be changed, and data[0] contains the 550 * value COMEDI_INPUT or COMEDI_OUTPUT. */ 551static int pcmuio_dio_insn_config(comedi_device * dev, comedi_subdevice * s, 552 comedi_insn * insn, unsigned int * data) 553{ 554 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no = 555 chan % 8; 556 unsigned long ioaddr; 557 unsigned char byte; 558 559 /* Compute ioaddr for this channel */ 560 ioaddr = subpriv->iobases[byte_no]; 561 562 /* NOTE: 563 writing a 0 an IO channel's bit sets the channel to INPUT 564 and pulls the line high as well 565 566 writing a 1 to an IO channel's bit pulls the line low 567 568 All channels are implicitly always in OUTPUT mode -- but when 569 they are high they can be considered to be in INPUT mode.. 570 571 Thus, we only force channels low if the config request was INPUT, 572 otherwise we do nothing to the hardware. */ 573 574 switch (data[0]) { 575 case INSN_CONFIG_DIO_OUTPUT: 576 /* save to io_bits -- don't actually do anything since 577 all input channels are also output channels... */ 578 s->io_bits |= 1 << chan; 579 break; 580 case INSN_CONFIG_DIO_INPUT: 581 /* write a 0 to the actual register representing the channel 582 to set it to 'input'. 0 means "float high". */ 583 byte = inb(ioaddr); 584 byte &= ~(1 << bit_no); 585 /**< set input channel to '0' */ 586 587 /* write out byte -- this is the only time we actually affect the 588 hardware as all channels are implicitly output -- but input 589 channels are set to float-high */ 590 outb(byte, ioaddr); 591 592 /* save to io_bits */ 593 s->io_bits &= ~(1 << chan); 594 break; 595 596 case INSN_CONFIG_DIO_QUERY: 597 /* retreive from shadow register */ 598 data[1] = 599 (s-> 600 io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; 601 return insn->n; 602 break; 603 604 default: 605 return -EINVAL; 606 break; 607 } 608 609 return insn->n; 610} 611 612static void init_asics(comedi_device * dev) 613{ /* sets up an 614 ASIC chip to defaults */ 615 int asic; 616 617 for (asic = 0; asic < thisboard->num_asics; ++asic) { 618 int port, page; 619 unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE; 620 621 switch_page(dev, asic, 0); /* switch back to page 0 */ 622 623 /* first, clear all the DIO port bits */ 624 for (port = 0; port < PORTS_PER_ASIC; ++port) 625 outb(0, baseaddr + REG_PORT0 + port); 626 627 /* Next, clear all the paged registers for each page */ 628 for (page = 1; page < NUM_PAGES; ++page) { 629 int reg; 630 /* now clear all the paged registers */ 631 switch_page(dev, asic, page); 632 for (reg = FIRST_PAGED_REG; 633 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg) 634 outb(0, baseaddr + reg); 635 } 636 637 /* DEBUG set rising edge interrupts on port0 of both asics */ 638 /*switch_page(dev, asic, PAGE_POL); 639 outb(0xff, baseaddr + REG_POL0); 640 switch_page(dev, asic, PAGE_ENAB); 641 outb(0xff, baseaddr + REG_ENAB0); */ 642 /* END DEBUG */ 643 644 switch_page(dev, asic, 0); /* switch back to default page 0 */ 645 646 } 647} 648 649static void switch_page(comedi_device * dev, int asic, int page) 650{ 651 if (asic < 0 || asic >= thisboard->num_asics) 652 return; /* paranoia */ 653 if (page < 0 || page >= NUM_PAGES) 654 return; /* more paranoia */ 655 656 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK; 657 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET; 658 659 /* now write out the shadow register */ 660 outb(devpriv->asics[asic].pagelock, 661 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); 662} 663 664#ifdef notused 665static void lock_port(comedi_device * dev, int asic, int port) 666{ 667 if (asic < 0 || asic >= thisboard->num_asics) 668 return; /* paranoia */ 669 if (port < 0 || port >= PORTS_PER_ASIC) 670 return; /* more paranoia */ 671 672 devpriv->asics[asic].pagelock |= 0x1 << port; 673 /* now write out the shadow register */ 674 outb(devpriv->asics[asic].pagelock, 675 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); 676} 677 678static void unlock_port(comedi_device * dev, int asic, int port) 679{ 680 if (asic < 0 || asic >= thisboard->num_asics) 681 return; /* paranoia */ 682 if (port < 0 || port >= PORTS_PER_ASIC) 683 return; /* more paranoia */ 684 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK; 685 /* now write out the shadow register */ 686 outb(devpriv->asics[asic].pagelock, 687 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); 688} 689#endif /* notused */ 690 691static irqreturn_t interrupt_pcmuio(int irq, void *d PT_REGS_ARG) 692{ 693 int asic, got1 = 0; 694 comedi_device *dev = (comedi_device *) d; 695 696 for (asic = 0; asic < MAX_ASICS; ++asic) { 697 if (irq == devpriv->asics[asic].irq) { 698 unsigned long flags; 699 unsigned triggered = 0; 700 unsigned long iobase = devpriv->asics[asic].iobase; 701 /* it is an interrupt for ASIC #asic */ 702 unsigned char int_pend; 703 704 comedi_spin_lock_irqsave(&devpriv->asics[asic].spinlock, 705 flags); 706 707 int_pend = inb(iobase + REG_INT_PENDING) & 0x07; 708 709 if (int_pend) { 710 int port; 711 for (port = 0; port < INTR_PORTS_PER_ASIC; 712 ++port) { 713 if (int_pend & (0x1 << port)) { 714 unsigned char 715 io_lines_with_edges = 0; 716 switch_page(dev, asic, 717 PAGE_INT_ID); 718 io_lines_with_edges = 719 inb(iobase + 720 REG_INT_ID0 + port); 721 722 if (io_lines_with_edges) 723 /* clear pending interrupt */ 724 outb(0, iobase + 725 REG_INT_ID0 + 726 port); 727 728 triggered |= 729 io_lines_with_edges << 730 port * 8; 731 } 732 } 733 734 ++got1; 735 } 736 737 comedi_spin_unlock_irqrestore(&devpriv->asics[asic]. 738 spinlock, flags); 739 740 if (triggered) { 741 comedi_subdevice *s; 742 /* TODO here: dispatch io lines to subdevs with commands.. */ 743 printk("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n", irq, asic, triggered); 744 for (s = dev->subdevices; 745 s < dev->subdevices + dev->n_subdevices; 746 ++s) { 747 if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */ 748 unsigned long flags; 749 unsigned oldevents; 750 751 comedi_spin_lock_irqsave 752 (&subpriv->intr. 753 spinlock, flags); 754 755 oldevents = s->async->events; 756 757 if (subpriv->intr.active) { 758 unsigned mytrig = 759 ((triggered >> 760 subpriv-> 761 intr. 762 asic_chan) 763 & ((0x1 << subpriv->intr.num_asic_chans) - 1)) << subpriv->intr.first_chan; 764 if (mytrig & subpriv-> 765 intr. 766 enabled_mask) { 767 unsigned int val = 768 0; 769 unsigned int n, 770 ch, len; 771 772 len = s->async-> 773 cmd. 774 chanlist_len; 775 for (n = 0; 776 n < len; 777 n++) { 778 ch = CR_CHAN(s->async->cmd.chanlist[n]); 779 if (mytrig & (1U << ch)) { 780 val |= (1U << n); 781 } 782 } 783 /* Write the scan to the buffer. */ 784 if (comedi_buf_put(s->async, ((short *) & val)[0]) 785 && 786 comedi_buf_put 787 (s->async, ((short *) & val)[1])) { 788 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS); 789 } else { 790 /* Overflow! Stop acquisition!! */ 791 /* TODO: STOP_ACQUISITION_CALL_HERE!! */ 792 pcmuio_stop_intr 793 (dev, 794 s); 795 } 796 797 /* Check for end of acquisition. */ 798 if (!subpriv-> 799 intr. 800 continuous) 801 { 802 /* stop_src == TRIG_COUNT */ 803 if (subpriv->intr.stop_count > 0) { 804 subpriv-> 805 intr. 806 stop_count--; 807 if (subpriv->intr.stop_count == 0) { 808 s->async->events |= COMEDI_CB_EOA; 809 /* TODO: STOP_ACQUISITION_CALL_HERE!! */ 810 pcmuio_stop_intr 811 (dev, 812 s); 813 } 814 } 815 } 816 } 817 } 818 819 comedi_spin_unlock_irqrestore 820 (&subpriv->intr. 821 spinlock, flags); 822 823 if (oldevents != 824 s->async->events) { 825 comedi_event(dev, s); 826 } 827 828 } 829 830 } 831 } 832 833 } 834 } 835 if (!got1) 836 return IRQ_NONE; /* interrupt from other source */ 837 return IRQ_HANDLED; 838} 839 840static void pcmuio_stop_intr(comedi_device * dev, comedi_subdevice * s) 841{ 842 int nports, firstport, asic, port; 843 844 if ((asic = subpriv->intr.asic) < 0) 845 return; /* not an interrupt subdev */ 846 847 subpriv->intr.enabled_mask = 0; 848 subpriv->intr.active = 0; 849 s->async->inttrig = 0; 850 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT; 851 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT; 852 switch_page(dev, asic, PAGE_ENAB); 853 for (port = firstport; port < firstport + nports; ++port) { 854 /* disable all intrs for this subdev.. */ 855 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port); 856 } 857} 858 859static int pcmuio_start_intr(comedi_device * dev, comedi_subdevice * s) 860{ 861 if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) { 862 /* An empty acquisition! */ 863 s->async->events |= COMEDI_CB_EOA; 864 subpriv->intr.active = 0; 865 return 1; 866 } else { 867 unsigned bits = 0, pol_bits = 0, n; 868 int nports, firstport, asic, port; 869 comedi_cmd *cmd = &s->async->cmd; 870 871 if ((asic = subpriv->intr.asic) < 0) 872 return 1; /* not an interrupt 873 subdev */ 874 subpriv->intr.enabled_mask = 0; 875 subpriv->intr.active = 1; 876 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT; 877 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT; 878 if (cmd->chanlist) { 879 for (n = 0; n < cmd->chanlist_len; n++) { 880 bits |= (1U << CR_CHAN(cmd->chanlist[n])); 881 pol_bits |= (CR_AREF(cmd->chanlist[n]) 882 || CR_RANGE(cmd->chanlist[n]) ? 1U : 0U) 883 << CR_CHAN(cmd->chanlist[n]); 884 } 885 } 886 bits &= ((0x1 << subpriv->intr.num_asic_chans) - 887 1) << subpriv->intr.first_chan; 888 subpriv->intr.enabled_mask = bits; 889 890 switch_page(dev, asic, PAGE_ENAB); 891 for (port = firstport; port < firstport + nports; ++port) { 892 unsigned enab = 893 bits >> (subpriv->intr.first_chan + (port - 894 firstport) * 8) & 0xff, pol = 895 pol_bits >> (subpriv->intr.first_chan + (port - 896 firstport) * 8) & 0xff; 897 /* set enab intrs for this subdev.. */ 898 outb(enab, 899 devpriv->asics[asic].iobase + REG_ENAB0 + port); 900 switch_page(dev, asic, PAGE_POL); 901 outb(pol, 902 devpriv->asics[asic].iobase + REG_ENAB0 + port); 903 } 904 } 905 return 0; 906} 907 908static int pcmuio_cancel(comedi_device * dev, comedi_subdevice * s) 909{ 910 unsigned long flags; 911 912 comedi_spin_lock_irqsave(&subpriv->intr.spinlock, flags); 913 if (subpriv->intr.active) 914 pcmuio_stop_intr(dev, s); 915 comedi_spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); 916 917 return 0; 918} 919 920/* 921 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice. 922 */ 923static int 924pcmuio_inttrig_start_intr(comedi_device * dev, comedi_subdevice * s, 925 unsigned int trignum) 926{ 927 unsigned long flags; 928 int event = 0; 929 930 if (trignum != 0) 931 return -EINVAL; 932 933 comedi_spin_lock_irqsave(&subpriv->intr.spinlock, flags); 934 s->async->inttrig = 0; 935 if (subpriv->intr.active) { 936 event = pcmuio_start_intr(dev, s); 937 } 938 comedi_spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); 939 940 if (event) { 941 comedi_event(dev, s); 942 } 943 944 return 1; 945} 946 947/* 948 * 'do_cmd' function for an 'INTERRUPT' subdevice. 949 */ 950static int pcmuio_cmd(comedi_device * dev, comedi_subdevice * s) 951{ 952 comedi_cmd *cmd = &s->async->cmd; 953 unsigned long flags; 954 int event = 0; 955 956 comedi_spin_lock_irqsave(&subpriv->intr.spinlock, flags); 957 subpriv->intr.active = 1; 958 959 /* Set up end of acquisition. */ 960 switch (cmd->stop_src) { 961 case TRIG_COUNT: 962 subpriv->intr.continuous = 0; 963 subpriv->intr.stop_count = cmd->stop_arg; 964 break; 965 default: 966 /* TRIG_NONE */ 967 subpriv->intr.continuous = 1; 968 subpriv->intr.stop_count = 0; 969 break; 970 } 971 972 /* Set up start of acquisition. */ 973 switch (cmd->start_src) { 974 case TRIG_INT: 975 s->async->inttrig = pcmuio_inttrig_start_intr; 976 break; 977 default: 978 /* TRIG_NOW */ 979 event = pcmuio_start_intr(dev, s); 980 break; 981 } 982 comedi_spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); 983 984 if (event) { 985 comedi_event(dev, s); 986 } 987 988 return 0; 989} 990 991/* 992 * 'do_cmdtest' function for an 'INTERRUPT' subdevice. 993 */ 994static int 995pcmuio_cmdtest(comedi_device * dev, comedi_subdevice * s, comedi_cmd * cmd) 996{ 997 int err = 0; 998 unsigned int tmp; 999 1000 /* step 1: make sure trigger sources are trivially valid */ 1001 1002 tmp = cmd->start_src; 1003 cmd->start_src &= (TRIG_NOW | TRIG_INT); 1004 if (!cmd->start_src || tmp != cmd->start_src) 1005 err++; 1006 1007 tmp = cmd->scan_begin_src; 1008 cmd->scan_begin_src &= TRIG_EXT; 1009 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) 1010 err++; 1011 1012 tmp = cmd->convert_src; 1013 cmd->convert_src &= TRIG_NOW; 1014 if (!cmd->convert_src || tmp != cmd->convert_src) 1015 err++; 1016 1017 tmp = cmd->scan_end_src; 1018 cmd->scan_end_src &= TRIG_COUNT; 1019 if (!cmd->scan_end_src || tmp != cmd->scan_end_src) 1020 err++; 1021 1022 tmp = cmd->stop_src; 1023 cmd->stop_src &= (TRIG_COUNT | TRIG_NONE); 1024 if (!cmd->stop_src || tmp != cmd->stop_src) 1025 err++; 1026 1027 if (err) 1028 return 1; 1029 1030 /* step 2: make sure trigger sources are unique and mutually compatible */ 1031 1032 /* these tests are true if more than one _src bit is set */ 1033 if ((cmd->start_src & (cmd->start_src - 1)) != 0) 1034 err++; 1035 if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0) 1036 err++; 1037 if ((cmd->convert_src & (cmd->convert_src - 1)) != 0) 1038 err++; 1039 if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0) 1040 err++; 1041 if ((cmd->stop_src & (cmd->stop_src - 1)) != 0) 1042 err++; 1043 1044 if (err) 1045 return 2; 1046 1047 /* step 3: make sure arguments are trivially compatible */ 1048 1049 /* cmd->start_src == TRIG_NOW || cmd->start_src == TRIG_INT */ 1050 if (cmd->start_arg != 0) { 1051 cmd->start_arg = 0; 1052 err++; 1053 } 1054 1055 /* cmd->scan_begin_src == TRIG_EXT */ 1056 if (cmd->scan_begin_arg != 0) { 1057 cmd->scan_begin_arg = 0; 1058 err++; 1059 } 1060 1061 /* cmd->convert_src == TRIG_NOW */ 1062 if (cmd->convert_arg != 0) { 1063 cmd->convert_arg = 0; 1064 err++; 1065 } 1066 1067 /* cmd->scan_end_src == TRIG_COUNT */ 1068 if (cmd->scan_end_arg != cmd->chanlist_len) { 1069 cmd->scan_end_arg = cmd->chanlist_len; 1070 err++; 1071 } 1072 1073 switch (cmd->stop_src) { 1074 case TRIG_COUNT: 1075 /* any count allowed */ 1076 break; 1077 case TRIG_NONE: 1078 if (cmd->stop_arg != 0) { 1079 cmd->stop_arg = 0; 1080 err++; 1081 } 1082 break; 1083 default: 1084 break; 1085 } 1086 1087 if (err) 1088 return 3; 1089 1090 /* step 4: fix up any arguments */ 1091 1092 /* if (err) return 4; */ 1093 1094 return 0; 1095} 1096 1097/* 1098 * A convenient macro that defines init_module() and cleanup_module(), 1099 * as necessary. 1100 */ 1101COMEDI_INITCLEANUP(driver); 1102