pcmuio.c revision 6b19f9c6bee7afcd1e5dee9528333b2aa52de404
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 <linux/interrupt.h> 79#include <linux/slab.h> 80#include "../comedidev.h" 81#include "pcm_common.h" 82 83#include <linux/pci.h> /* for PCI devices */ 84 85#define CHANS_PER_PORT 8 86#define PORTS_PER_ASIC 6 87#define INTR_PORTS_PER_ASIC 3 88#define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */ 89#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT) 90#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC) 91#define INTR_CHANS_PER_ASIC 24 92#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT) 93#define MAX_DIO_CHANS (PORTS_PER_ASIC*2*CHANS_PER_PORT) 94#define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC) 95#define SDEV_NO ((int)(s - dev->subdevices)) 96#define CALC_N_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/) 97/* IO Memory sizes */ 98#define ASIC_IOSIZE (0x10) 99#define PCMUIO48_IOSIZE ASIC_IOSIZE 100#define PCMUIO96_IOSIZE (ASIC_IOSIZE*2) 101 102/* Some offsets - these are all in the 16byte IO memory offset from 103 the base address. Note that there is a paging scheme to swap out 104 offsets 0x8-0xA using the PAGELOCK register. See the table below. 105 106 Register(s) Pages R/W? Description 107 -------------------------------------------------------------- 108 REG_PORTx All R/W Read/Write/Configure IO 109 REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int. 110 REG_PAGELOCK All WriteOnly Select a page 111 REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity 112 REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int. 113 REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints. 114 */ 115#define REG_PORT0 0x0 116#define REG_PORT1 0x1 117#define REG_PORT2 0x2 118#define REG_PORT3 0x3 119#define REG_PORT4 0x4 120#define REG_PORT5 0x5 121#define REG_INT_PENDING 0x6 122#define REG_PAGELOCK 0x7 /* page selector register, upper 2 bits select a page 123 and bits 0-5 are used to 'lock down' a particular 124 port above to make it readonly. */ 125#define REG_POL0 0x8 126#define REG_POL1 0x9 127#define REG_POL2 0xA 128#define REG_ENAB0 0x8 129#define REG_ENAB1 0x9 130#define REG_ENAB2 0xA 131#define REG_INT_ID0 0x8 132#define REG_INT_ID1 0x9 133#define REG_INT_ID2 0xA 134 135#define NUM_PAGED_REGS 3 136#define NUM_PAGES 4 137#define FIRST_PAGED_REG 0x8 138#define REG_PAGE_BITOFFSET 6 139#define REG_LOCK_BITOFFSET 0 140#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1)) 141#define REG_LOCK_MASK ~(REG_PAGE_MASK) 142#define PAGE_POL 1 143#define PAGE_ENAB 2 144#define PAGE_INT_ID 3 145 146/* 147 * Board descriptions for two imaginary boards. Describing the 148 * boards in this way is optional, and completely driver-dependent. 149 * Some drivers use arrays such as this, other do not. 150 */ 151struct pcmuio_board { 152 const char *name; 153 const int num_asics; 154 const int num_channels_per_port; 155 const int num_ports; 156}; 157 158/* 159 * Useful for shorthand access to the particular board structure 160 */ 161#define thisboard ((const struct pcmuio_board *)dev->board_ptr) 162 163/* this structure is for data unique to this subdevice. */ 164struct pcmuio_subdev_private { 165 /* mapping of halfwords (bytes) in port/chanarray to iobase */ 166 unsigned long iobases[PORTS_PER_SUBDEV]; 167 168 /* The below is only used for intr subdevices */ 169 struct { 170 int asic; /* if non-negative, this subdev has an interrupt asic */ 171 int first_chan; /* if nonnegative, the first channel id for 172 interrupts. */ 173 int num_asic_chans; /* the number of asic channels in this subdev 174 that have interrutps */ 175 int asic_chan; /* if nonnegative, the first channel id with 176 respect to the asic that has interrupts */ 177 int enabled_mask; /* subdev-relative channel mask for channels 178 we are interested in */ 179 int active; 180 int stop_count; 181 int continuous; 182 spinlock_t spinlock; 183 } intr; 184}; 185 186/* this structure is for data unique to this hardware driver. If 187 several hardware drivers keep similar information in this structure, 188 feel free to suggest moving the variable to the struct comedi_device struct. */ 189struct pcmuio_private { 190 struct { 191 unsigned char pagelock; /* current page and lock */ 192 unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */ 193 unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */ 194 int num; 195 unsigned long iobase; 196 unsigned int irq; 197 spinlock_t spinlock; 198 } asics[MAX_ASICS]; 199 struct pcmuio_subdev_private *sprivs; 200}; 201 202/* 203 * most drivers define the following macro to make it easy to 204 * access the private structure. 205 */ 206#define devpriv ((struct pcmuio_private *)dev->private) 207#define subpriv ((struct pcmuio_subdev_private *)s->private) 208 209/* DIO devices are slightly special. Although it is possible to 210 * implement the insn_read/insn_write interface, it is much more 211 * useful to applications if you implement the insn_bits interface. 212 * This allows packed reading/writing of the DIO channels. The 213 * comedi core can convert between insn_bits and insn_read/write */ 214static int pcmuio_dio_insn_bits(struct comedi_device *dev, 215 struct comedi_subdevice *s, 216 struct comedi_insn *insn, unsigned int *data) 217{ 218 int byte_no; 219 if (insn->n != 2) 220 return -EINVAL; 221 222 /* NOTE: 223 reading a 0 means this channel was high 224 writine a 0 sets the channel high 225 reading a 1 means this channel was low 226 writing a 1 means set this channel low 227 228 Therefore everything is always inverted. */ 229 230 /* The insn data is a mask in data[0] and the new data 231 * in data[1], each channel cooresponding to a bit. */ 232 233#ifdef DAMMIT_ITS_BROKEN 234 /* DEBUG */ 235 dev_dbg(dev->hw_dev, "write mask: %08x data: %08x\n", data[0], 236 data[1]); 237#endif 238 239 s->state = 0; 240 241 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) { 242 /* address of 8-bit port */ 243 unsigned long ioaddr = subpriv->iobases[byte_no], 244 /* bit offset of port in 32-bit doubleword */ 245 offset = byte_no * 8; 246 /* this 8-bit port's data */ 247 unsigned char byte = 0, 248 /* The write mask for this port (if any) */ 249 write_mask_byte = (data[0] >> offset) & 0xff, 250 /* The data byte for this port */ 251 data_byte = (data[1] >> offset) & 0xff; 252 253 byte = inb(ioaddr); /* read all 8-bits for this port */ 254 255#ifdef DAMMIT_ITS_BROKEN 256 /* DEBUG */ 257 printk 258 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ", 259 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte, 260 offset, ioaddr, (unsigned)byte); 261#endif 262 263 if (write_mask_byte) { 264 /* this byte has some write_bits -- so set the output lines */ 265 byte &= ~write_mask_byte; /* clear bits for write mask */ 266 byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */ 267 /* Write out the new digital output state */ 268 outb(byte, ioaddr); 269 } 270#ifdef DAMMIT_ITS_BROKEN 271 /* DEBUG */ 272 dev_dbg(dev->hw_dev, "data_out_byte %02x\n", (unsigned)byte); 273#endif 274 /* save the digital input lines for this byte.. */ 275 s->state |= ((unsigned int)byte) << offset; 276 } 277 278 /* now return the DIO lines to data[1] - note they came inverted! */ 279 data[1] = ~s->state; 280 281#ifdef DAMMIT_ITS_BROKEN 282 /* DEBUG */ 283 dev_dbg(dev->hw_dev, "s->state %08x data_out %08x\n", s->state, 284 data[1]); 285#endif 286 287 return 2; 288} 289 290/* The input or output configuration of each digital line is 291 * configured by a special insn_config instruction. chanspec 292 * contains the channel to be changed, and data[0] contains the 293 * value COMEDI_INPUT or COMEDI_OUTPUT. */ 294static int pcmuio_dio_insn_config(struct comedi_device *dev, 295 struct comedi_subdevice *s, 296 struct comedi_insn *insn, unsigned int *data) 297{ 298 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no = 299 chan % 8; 300 unsigned long ioaddr; 301 unsigned char byte; 302 303 /* Compute ioaddr for this channel */ 304 ioaddr = subpriv->iobases[byte_no]; 305 306 /* NOTE: 307 writing a 0 an IO channel's bit sets the channel to INPUT 308 and pulls the line high as well 309 310 writing a 1 to an IO channel's bit pulls the line low 311 312 All channels are implicitly always in OUTPUT mode -- but when 313 they are high they can be considered to be in INPUT mode.. 314 315 Thus, we only force channels low if the config request was INPUT, 316 otherwise we do nothing to the hardware. */ 317 318 switch (data[0]) { 319 case INSN_CONFIG_DIO_OUTPUT: 320 /* save to io_bits -- don't actually do anything since 321 all input channels are also output channels... */ 322 s->io_bits |= 1 << chan; 323 break; 324 case INSN_CONFIG_DIO_INPUT: 325 /* write a 0 to the actual register representing the channel 326 to set it to 'input'. 0 means "float high". */ 327 byte = inb(ioaddr); 328 byte &= ~(1 << bit_no); 329 /**< set input channel to '0' */ 330 331 /* write out byte -- this is the only time we actually affect the 332 hardware as all channels are implicitly output -- but input 333 channels are set to float-high */ 334 outb(byte, ioaddr); 335 336 /* save to io_bits */ 337 s->io_bits &= ~(1 << chan); 338 break; 339 340 case INSN_CONFIG_DIO_QUERY: 341 /* retrieve from shadow register */ 342 data[1] = 343 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; 344 return insn->n; 345 break; 346 347 default: 348 return -EINVAL; 349 break; 350 } 351 352 return insn->n; 353} 354 355static void switch_page(struct comedi_device *dev, int asic, int page) 356{ 357 if (asic < 0 || asic >= thisboard->num_asics) 358 return; /* paranoia */ 359 if (page < 0 || page >= NUM_PAGES) 360 return; /* more paranoia */ 361 362 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK; 363 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET; 364 365 /* now write out the shadow register */ 366 outb(devpriv->asics[asic].pagelock, 367 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); 368} 369 370static void init_asics(struct comedi_device *dev) 371{ /* sets up an 372 ASIC chip to defaults */ 373 int asic; 374 375 for (asic = 0; asic < thisboard->num_asics; ++asic) { 376 int port, page; 377 unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE; 378 379 switch_page(dev, asic, 0); /* switch back to page 0 */ 380 381 /* first, clear all the DIO port bits */ 382 for (port = 0; port < PORTS_PER_ASIC; ++port) 383 outb(0, baseaddr + REG_PORT0 + port); 384 385 /* Next, clear all the paged registers for each page */ 386 for (page = 1; page < NUM_PAGES; ++page) { 387 int reg; 388 /* now clear all the paged registers */ 389 switch_page(dev, asic, page); 390 for (reg = FIRST_PAGED_REG; 391 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg) 392 outb(0, baseaddr + reg); 393 } 394 395 /* DEBUG set rising edge interrupts on port0 of both asics */ 396 /*switch_page(dev, asic, PAGE_POL); 397 outb(0xff, baseaddr + REG_POL0); 398 switch_page(dev, asic, PAGE_ENAB); 399 outb(0xff, baseaddr + REG_ENAB0); */ 400 /* END DEBUG */ 401 402 switch_page(dev, asic, 0); /* switch back to default page 0 */ 403 404 } 405} 406 407#ifdef notused 408static void lock_port(struct comedi_device *dev, int asic, int port) 409{ 410 if (asic < 0 || asic >= thisboard->num_asics) 411 return; /* paranoia */ 412 if (port < 0 || port >= PORTS_PER_ASIC) 413 return; /* more paranoia */ 414 415 devpriv->asics[asic].pagelock |= 0x1 << port; 416 /* now write out the shadow register */ 417 outb(devpriv->asics[asic].pagelock, 418 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); 419} 420 421static void unlock_port(struct comedi_device *dev, int asic, int port) 422{ 423 if (asic < 0 || asic >= thisboard->num_asics) 424 return; /* paranoia */ 425 if (port < 0 || port >= PORTS_PER_ASIC) 426 return; /* more paranoia */ 427 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK; 428 /* now write out the shadow register */ 429 outb(devpriv->asics[asic].pagelock, 430 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); 431} 432#endif /* notused */ 433 434static void pcmuio_stop_intr(struct comedi_device *dev, 435 struct comedi_subdevice *s) 436{ 437 int nports, firstport, asic, port; 438 439 asic = subpriv->intr.asic; 440 if (asic < 0) 441 return; /* not an interrupt subdev */ 442 443 subpriv->intr.enabled_mask = 0; 444 subpriv->intr.active = 0; 445 s->async->inttrig = 0; 446 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT; 447 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT; 448 switch_page(dev, asic, PAGE_ENAB); 449 for (port = firstport; port < firstport + nports; ++port) { 450 /* disable all intrs for this subdev.. */ 451 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port); 452 } 453} 454 455static irqreturn_t interrupt_pcmuio(int irq, void *d) 456{ 457 int asic, got1 = 0; 458 struct comedi_device *dev = (struct comedi_device *)d; 459 460 for (asic = 0; asic < MAX_ASICS; ++asic) { 461 if (irq == devpriv->asics[asic].irq) { 462 unsigned long flags; 463 unsigned triggered = 0; 464 unsigned long iobase = devpriv->asics[asic].iobase; 465 /* it is an interrupt for ASIC #asic */ 466 unsigned char int_pend; 467 468 spin_lock_irqsave(&devpriv->asics[asic].spinlock, 469 flags); 470 471 int_pend = inb(iobase + REG_INT_PENDING) & 0x07; 472 473 if (int_pend) { 474 int port; 475 for (port = 0; port < INTR_PORTS_PER_ASIC; 476 ++port) { 477 if (int_pend & (0x1 << port)) { 478 unsigned char 479 io_lines_with_edges = 0; 480 switch_page(dev, asic, 481 PAGE_INT_ID); 482 io_lines_with_edges = 483 inb(iobase + 484 REG_INT_ID0 + port); 485 486 if (io_lines_with_edges) 487 /* clear pending interrupt */ 488 outb(0, iobase + 489 REG_INT_ID0 + 490 port); 491 492 triggered |= 493 io_lines_with_edges << 494 port * 8; 495 } 496 } 497 498 ++got1; 499 } 500 501 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock, 502 flags); 503 504 if (triggered) { 505 struct comedi_subdevice *s; 506 /* TODO here: dispatch io lines to subdevs with commands.. */ 507 printk 508 ("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n", 509 irq, asic, triggered); 510 for (s = dev->subdevices; 511 s < dev->subdevices + dev->n_subdevices; 512 ++s) { 513 if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */ 514 unsigned long flags; 515 unsigned oldevents; 516 517 spin_lock_irqsave(&subpriv-> 518 intr.spinlock, 519 flags); 520 521 oldevents = s->async->events; 522 523 if (subpriv->intr.active) { 524 unsigned mytrig = 525 ((triggered >> 526 subpriv->intr.asic_chan) 527 & 528 ((0x1 << subpriv-> 529 intr. 530 num_asic_chans) - 531 1)) << subpriv-> 532 intr.first_chan; 533 if (mytrig & 534 subpriv->intr.enabled_mask) 535 { 536 unsigned int val 537 = 0; 538 unsigned int n, 539 ch, len; 540 541 len = 542 s-> 543 async->cmd.chanlist_len; 544 for (n = 0; 545 n < len; 546 n++) { 547 ch = CR_CHAN(s->async->cmd.chanlist[n]); 548 if (mytrig & (1U << ch)) { 549 val |= (1U << n); 550 } 551 } 552 /* Write the scan to the buffer. */ 553 if (comedi_buf_put(s->async, ((short *)&val)[0]) 554 && 555 comedi_buf_put 556 (s->async, 557 ((short *) 558 &val)[1])) 559 { 560 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS); 561 } else { 562 /* Overflow! Stop acquisition!! */ 563 /* TODO: STOP_ACQUISITION_CALL_HERE!! */ 564 pcmuio_stop_intr 565 (dev, 566 s); 567 } 568 569 /* Check for end of acquisition. */ 570 if (!subpriv->intr.continuous) { 571 /* stop_src == TRIG_COUNT */ 572 if (subpriv->intr.stop_count > 0) { 573 subpriv->intr.stop_count--; 574 if (subpriv->intr.stop_count == 0) { 575 s->async->events |= COMEDI_CB_EOA; 576 /* TODO: STOP_ACQUISITION_CALL_HERE!! */ 577 pcmuio_stop_intr 578 (dev, 579 s); 580 } 581 } 582 } 583 } 584 } 585 586 spin_unlock_irqrestore 587 (&subpriv->intr.spinlock, 588 flags); 589 590 if (oldevents != 591 s->async->events) { 592 comedi_event(dev, s); 593 } 594 595 } 596 597 } 598 } 599 600 } 601 } 602 if (!got1) 603 return IRQ_NONE; /* interrupt from other source */ 604 return IRQ_HANDLED; 605} 606 607static int pcmuio_start_intr(struct comedi_device *dev, 608 struct comedi_subdevice *s) 609{ 610 if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) { 611 /* An empty acquisition! */ 612 s->async->events |= COMEDI_CB_EOA; 613 subpriv->intr.active = 0; 614 return 1; 615 } else { 616 unsigned bits = 0, pol_bits = 0, n; 617 int nports, firstport, asic, port; 618 struct comedi_cmd *cmd = &s->async->cmd; 619 620 asic = subpriv->intr.asic; 621 if (asic < 0) 622 return 1; /* not an interrupt 623 subdev */ 624 subpriv->intr.enabled_mask = 0; 625 subpriv->intr.active = 1; 626 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT; 627 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT; 628 if (cmd->chanlist) { 629 for (n = 0; n < cmd->chanlist_len; n++) { 630 bits |= (1U << CR_CHAN(cmd->chanlist[n])); 631 pol_bits |= (CR_AREF(cmd->chanlist[n]) 632 || CR_RANGE(cmd-> 633 chanlist[n]) ? 1U : 0U) 634 << CR_CHAN(cmd->chanlist[n]); 635 } 636 } 637 bits &= ((0x1 << subpriv->intr.num_asic_chans) - 638 1) << subpriv->intr.first_chan; 639 subpriv->intr.enabled_mask = bits; 640 641 switch_page(dev, asic, PAGE_ENAB); 642 for (port = firstport; port < firstport + nports; ++port) { 643 unsigned enab = 644 bits >> (subpriv->intr.first_chan + (port - 645 firstport) * 646 8) & 0xff, pol = 647 pol_bits >> (subpriv->intr.first_chan + 648 (port - firstport) * 8) & 0xff; 649 /* set enab intrs for this subdev.. */ 650 outb(enab, 651 devpriv->asics[asic].iobase + REG_ENAB0 + port); 652 switch_page(dev, asic, PAGE_POL); 653 outb(pol, 654 devpriv->asics[asic].iobase + REG_ENAB0 + port); 655 } 656 } 657 return 0; 658} 659 660static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s) 661{ 662 unsigned long flags; 663 664 spin_lock_irqsave(&subpriv->intr.spinlock, flags); 665 if (subpriv->intr.active) 666 pcmuio_stop_intr(dev, s); 667 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); 668 669 return 0; 670} 671 672/* 673 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice. 674 */ 675static int 676pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s, 677 unsigned int trignum) 678{ 679 unsigned long flags; 680 int event = 0; 681 682 if (trignum != 0) 683 return -EINVAL; 684 685 spin_lock_irqsave(&subpriv->intr.spinlock, flags); 686 s->async->inttrig = 0; 687 if (subpriv->intr.active) 688 event = pcmuio_start_intr(dev, s); 689 690 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); 691 692 if (event) 693 comedi_event(dev, s); 694 695 return 1; 696} 697 698/* 699 * 'do_cmd' function for an 'INTERRUPT' subdevice. 700 */ 701static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 702{ 703 struct comedi_cmd *cmd = &s->async->cmd; 704 unsigned long flags; 705 int event = 0; 706 707 spin_lock_irqsave(&subpriv->intr.spinlock, flags); 708 subpriv->intr.active = 1; 709 710 /* Set up end of acquisition. */ 711 switch (cmd->stop_src) { 712 case TRIG_COUNT: 713 subpriv->intr.continuous = 0; 714 subpriv->intr.stop_count = cmd->stop_arg; 715 break; 716 default: 717 /* TRIG_NONE */ 718 subpriv->intr.continuous = 1; 719 subpriv->intr.stop_count = 0; 720 break; 721 } 722 723 /* Set up start of acquisition. */ 724 switch (cmd->start_src) { 725 case TRIG_INT: 726 s->async->inttrig = pcmuio_inttrig_start_intr; 727 break; 728 default: 729 /* TRIG_NOW */ 730 event = pcmuio_start_intr(dev, s); 731 break; 732 } 733 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); 734 735 if (event) 736 comedi_event(dev, s); 737 738 return 0; 739} 740 741static int 742pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, 743 struct comedi_cmd *cmd) 744{ 745 return comedi_pcm_cmdtest(dev, s, cmd); 746} 747 748static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it) 749{ 750 struct comedi_subdevice *s; 751 int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0; 752 unsigned long iobase; 753 unsigned int irq[MAX_ASICS]; 754 755 iobase = it->options[0]; 756 irq[0] = it->options[1]; 757 irq[1] = it->options[2]; 758 759 dev_dbg(dev->hw_dev, "comedi%d: %s: io: %lx attached\n", dev->minor, 760 driver.driver_name, iobase); 761 762 dev->iobase = iobase; 763 764 if (!iobase || !request_region(iobase, 765 thisboard->num_asics * ASIC_IOSIZE, 766 driver.driver_name)) { 767 dev_err(dev->hw_dev, "I/O port conflict\n"); 768 return -EIO; 769 } 770 771/* 772 * Initialize dev->board_name. Note that we can use the "thisboard" 773 * macro now, since we just initialized it in the last line. 774 */ 775 dev->board_name = thisboard->name; 776 777/* 778 * Allocate the private structure area. alloc_private() is a 779 * convenient macro defined in comedidev.h. 780 */ 781 if (alloc_private(dev, sizeof(struct pcmuio_private)) < 0) { 782 dev_warn(dev->hw_dev, "cannot allocate private data structure\n"); 783 return -ENOMEM; 784 } 785 786 for (asic = 0; asic < MAX_ASICS; ++asic) { 787 devpriv->asics[asic].num = asic; 788 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE; 789 devpriv->asics[asic].irq = 0; /* this gets actually set at the end of 790 this function when we 791 request_irqs */ 792 spin_lock_init(&devpriv->asics[asic].spinlock); 793 } 794 795 chans_left = CHANS_PER_ASIC * thisboard->num_asics; 796 n_subdevs = CALC_N_SUBDEVS(chans_left); 797 devpriv->sprivs = 798 kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private), 799 GFP_KERNEL); 800 if (!devpriv->sprivs) { 801 dev_warn(dev->hw_dev, "cannot allocate subdevice private data structures\n"); 802 return -ENOMEM; 803 } 804 /* 805 * Allocate the subdevice structures. alloc_subdevice() is a 806 * convenient macro defined in comedidev.h. 807 * 808 * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the 809 * 96-channel version of the board. 810 */ 811 if (alloc_subdevices(dev, n_subdevs) < 0) { 812 dev_dbg(dev->hw_dev, "cannot allocate subdevice data structures\n"); 813 return -ENOMEM; 814 } 815 816 port = 0; 817 asic = 0; 818 for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) { 819 int byte_no; 820 821 s = dev->subdevices + sdev_no; 822 s->private = devpriv->sprivs + sdev_no; 823 s->maxdata = 1; 824 s->range_table = &range_digital; 825 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 826 s->type = COMEDI_SUBD_DIO; 827 s->insn_bits = pcmuio_dio_insn_bits; 828 s->insn_config = pcmuio_dio_insn_config; 829 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV); 830 subpriv->intr.asic = -1; 831 subpriv->intr.first_chan = -1; 832 subpriv->intr.asic_chan = -1; 833 subpriv->intr.num_asic_chans = -1; 834 subpriv->intr.active = 0; 835 s->len_chanlist = 1; 836 837 /* save the ioport address for each 'port' of 8 channels in the 838 subdevice */ 839 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) { 840 if (port >= PORTS_PER_ASIC) { 841 port = 0; 842 ++asic; 843 thisasic_chanct = 0; 844 } 845 subpriv->iobases[byte_no] = 846 devpriv->asics[asic].iobase + port; 847 848 if (thisasic_chanct < 849 CHANS_PER_PORT * INTR_PORTS_PER_ASIC 850 && subpriv->intr.asic < 0) { 851 /* this is an interrupt subdevice, so setup the struct */ 852 subpriv->intr.asic = asic; 853 subpriv->intr.active = 0; 854 subpriv->intr.stop_count = 0; 855 subpriv->intr.first_chan = byte_no * 8; 856 subpriv->intr.asic_chan = thisasic_chanct; 857 subpriv->intr.num_asic_chans = 858 s->n_chan - subpriv->intr.first_chan; 859 dev->read_subdev = s; 860 s->subdev_flags |= SDF_CMD_READ; 861 s->cancel = pcmuio_cancel; 862 s->do_cmd = pcmuio_cmd; 863 s->do_cmdtest = pcmuio_cmdtest; 864 s->len_chanlist = subpriv->intr.num_asic_chans; 865 } 866 thisasic_chanct += CHANS_PER_PORT; 867 } 868 spin_lock_init(&subpriv->intr.spinlock); 869 870 chans_left -= s->n_chan; 871 872 if (!chans_left) { 873 asic = 0; /* reset the asic to our first asic, to do intr subdevs */ 874 port = 0; 875 } 876 877 } 878 879 init_asics(dev); /* clear out all the registers, basically */ 880 881 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) { 882 if (irq[asic] 883 && request_irq(irq[asic], interrupt_pcmuio, 884 IRQF_SHARED, thisboard->name, dev)) { 885 int i; 886 /* unroll the allocated irqs.. */ 887 for (i = asic - 1; i >= 0; --i) { 888 free_irq(irq[i], dev); 889 devpriv->asics[i].irq = irq[i] = 0; 890 } 891 irq[asic] = 0; 892 } 893 devpriv->asics[asic].irq = irq[asic]; 894 } 895 896 dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple 897 irqs.. */ 898 899 if (irq[0]) { 900 dev_dbg(dev->hw_dev, "irq: %u\n", irq[0]); 901 if (irq[1] && thisboard->num_asics == 2) 902 dev_dbg(dev->hw_dev, "second ASIC irq: %u\n", irq[1]); 903 } else { 904 dev_dbg(dev->hw_dev, "(IRQ mode disabled)\n"); 905 } 906 907 908 return 1; 909} 910 911static int pcmuio_detach(struct comedi_device *dev) 912{ 913 int i; 914 915 dev_dbg(dev->hw_dev, "comedi%d: %s: remove\n", dev->minor, 916 driver.driver_name); 917 if (dev->iobase) 918 release_region(dev->iobase, ASIC_IOSIZE * thisboard->num_asics); 919 920 for (i = 0; i < MAX_ASICS; ++i) { 921 if (devpriv->asics[i].irq) 922 free_irq(devpriv->asics[i].irq, dev); 923 } 924 925 if (devpriv && devpriv->sprivs) 926 kfree(devpriv->sprivs); 927 928 return 0; 929} 930 931static const struct pcmuio_board pcmuio_boards[] = { 932 { 933 .name = "pcmuio48", 934 .num_asics = 1, 935 .num_ports = 6, 936 }, { 937 .name = "pcmuio96", 938 .num_asics = 2, 939 .num_ports = 12, 940 }, 941}; 942 943static struct comedi_driver driver = { 944 .driver_name = "pcmuio", 945 .module = THIS_MODULE, 946 .attach = pcmuio_attach, 947 .detach = pcmuio_detach, 948 .board_name = &pcmuio_boards[0].name, 949 .offset = sizeof(struct pcmuio_board), 950 .num_names = ARRAY_SIZE(pcmuio_boards), 951}; 952 953static int __init driver_init_module(void) 954{ 955 return comedi_driver_register(&driver); 956} 957module_init(driver_init_module); 958 959static void __exit driver_cleanup_module(void) 960{ 961 comedi_driver_unregister(&driver); 962} 963module_exit(driver_cleanup_module); 964 965MODULE_AUTHOR("Comedi http://www.comedi.org"); 966MODULE_DESCRIPTION("Comedi low-level driver"); 967MODULE_LICENSE("GPL"); 968