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