1/* 2 * pcmmio.c 3 * Driver for Winsystems PC-104 based multifunction IO board. 4 * 5 * COMEDI - Linux Control and Measurement Device Interface 6 * Copyright (C) 2007 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 19/* 20 * Driver: pcmmio 21 * Description: A driver for the PCM-MIO multifunction board 22 * Devices: (Winsystems) PCM-MIO [pcmmio] 23 * Author: Calin Culianu <calin@ajvar.org> 24 * Updated: Wed, May 16 2007 16:21:10 -0500 25 * Status: works 26 * 27 * A driver for the PCM-MIO multifunction board from Winsystems. This 28 * is a PC-104 based I/O board. It contains four subdevices: 29 * 30 * subdevice 0 - 16 channels of 16-bit AI 31 * subdevice 1 - 8 channels of 16-bit AO 32 * subdevice 2 - first 24 channels of the 48 channel of DIO 33 * (with edge-triggered interrupt support) 34 * subdevice 3 - last 24 channels of the 48 channel DIO 35 * (no interrupt support for this bank of channels) 36 * 37 * Some notes: 38 * 39 * Synchronous reads and writes are the only things implemented for analog 40 * input and output. The hardware itself can do streaming acquisition, etc. 41 * 42 * Asynchronous I/O for the DIO subdevices *is* implemented, however! They 43 * are basically edge-triggered interrupts for any configuration of the 44 * channels in subdevice 2. 45 * 46 * Also note that this interrupt support is untested. 47 * 48 * A few words about edge-detection IRQ support (commands on DIO): 49 * 50 * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ 51 * of the board to the comedi_config command. The board IRQ is not jumpered 52 * but rather configured through software, so any IRQ from 1-15 is OK. 53 * 54 * Due to the genericity of the comedi API, you need to create a special 55 * comedi_command in order to use edge-triggered interrupts for DIO. 56 * 57 * Use comedi_commands with TRIG_NOW. Your callback will be called each 58 * time an edge is detected on the specified DIO line(s), and the data 59 * values will be two sample_t's, which should be concatenated to form 60 * one 32-bit unsigned int. This value is the mask of channels that had 61 * edges detected from your channel list. Note that the bits positions 62 * in the mask correspond to positions in your chanlist when you 63 * specified the command and *not* channel id's! 64 * 65 * To set the polarity of the edge-detection interrupts pass a nonzero value 66 * for either CR_RANGE or CR_AREF for edge-up polarity, or a zero 67 * value for both CR_RANGE and CR_AREF if you want edge-down polarity. 68 * 69 * Configuration Options: 70 * [0] - I/O port base address 71 * [1] - IRQ (optional -- for edge-detect interrupt support only, 72 * leave out if you don't need this feature) 73 */ 74 75#include <linux/module.h> 76#include <linux/interrupt.h> 77#include <linux/slab.h> 78 79#include "../comedidev.h" 80 81#include "comedi_fc.h" 82 83/* 84 * Register I/O map 85 */ 86#define PCMMIO_AI_LSB_REG 0x00 87#define PCMMIO_AI_MSB_REG 0x01 88#define PCMMIO_AI_CMD_REG 0x02 89#define PCMMIO_AI_CMD_SE (1 << 7) 90#define PCMMIO_AI_CMD_ODD_CHAN (1 << 6) 91#define PCMMIO_AI_CMD_CHAN_SEL(x) (((x) & 0x3) << 4) 92#define PCMMIO_AI_CMD_RANGE(x) (((x) & 0x3) << 2) 93#define PCMMIO_RESOURCE_REG 0x02 94#define PCMMIO_RESOURCE_IRQ(x) (((x) & 0xf) << 0) 95#define PCMMIO_AI_STATUS_REG 0x03 96#define PCMMIO_AI_STATUS_DATA_READY (1 << 7) 97#define PCMMIO_AI_STATUS_DATA_DMA_PEND (1 << 6) 98#define PCMMIO_AI_STATUS_CMD_DMA_PEND (1 << 5) 99#define PCMMIO_AI_STATUS_IRQ_PEND (1 << 4) 100#define PCMMIO_AI_STATUS_DATA_DRQ_ENA (1 << 2) 101#define PCMMIO_AI_STATUS_REG_SEL (1 << 3) 102#define PCMMIO_AI_STATUS_CMD_DRQ_ENA (1 << 1) 103#define PCMMIO_AI_STATUS_IRQ_ENA (1 << 0) 104#define PCMMIO_AI_RES_ENA_REG 0x03 105#define PCMMIO_AI_RES_ENA_CMD_REG_ACCESS (0 << 3) 106#define PCMMIO_AI_RES_ENA_AI_RES_ACCESS (1 << 3) 107#define PCMMIO_AI_RES_ENA_DIO_RES_ACCESS (1 << 4) 108#define PCMMIO_AI_2ND_ADC_OFFSET 0x04 109 110#define PCMMIO_AO_LSB_REG 0x08 111#define PCMMIO_AO_LSB_SPAN(x) (((x) & 0xf) << 0) 112#define PCMMIO_AO_MSB_REG 0x09 113#define PCMMIO_AO_CMD_REG 0x0a 114#define PCMMIO_AO_CMD_WR_SPAN (0x2 << 4) 115#define PCMMIO_AO_CMD_WR_CODE (0x3 << 4) 116#define PCMMIO_AO_CMD_UPDATE (0x4 << 4) 117#define PCMMIO_AO_CMD_UPDATE_ALL (0x5 << 4) 118#define PCMMIO_AO_CMD_WR_SPAN_UPDATE (0x6 << 4) 119#define PCMMIO_AO_CMD_WR_CODE_UPDATE (0x7 << 4) 120#define PCMMIO_AO_CMD_WR_SPAN_UPDATE_ALL (0x8 << 4) 121#define PCMMIO_AO_CMD_WR_CODE_UPDATE_ALL (0x9 << 4) 122#define PCMMIO_AO_CMD_RD_B1_SPAN (0xa << 4) 123#define PCMMIO_AO_CMD_RD_B1_CODE (0xb << 4) 124#define PCMMIO_AO_CMD_RD_B2_SPAN (0xc << 4) 125#define PCMMIO_AO_CMD_RD_B2_CODE (0xd << 4) 126#define PCMMIO_AO_CMD_NOP (0xf << 4) 127#define PCMMIO_AO_CMD_CHAN_SEL(x) (((x) & 0x03) << 1) 128#define PCMMIO_AO_CMD_CHAN_SEL_ALL (0x0f << 0) 129#define PCMMIO_AO_STATUS_REG 0x0b 130#define PCMMIO_AO_STATUS_DATA_READY (1 << 7) 131#define PCMMIO_AO_STATUS_DATA_DMA_PEND (1 << 6) 132#define PCMMIO_AO_STATUS_CMD_DMA_PEND (1 << 5) 133#define PCMMIO_AO_STATUS_IRQ_PEND (1 << 4) 134#define PCMMIO_AO_STATUS_DATA_DRQ_ENA (1 << 2) 135#define PCMMIO_AO_STATUS_REG_SEL (1 << 3) 136#define PCMMIO_AO_STATUS_CMD_DRQ_ENA (1 << 1) 137#define PCMMIO_AO_STATUS_IRQ_ENA (1 << 0) 138#define PCMMIO_AO_RESOURCE_ENA_REG 0x0b 139#define PCMMIO_AO_2ND_DAC_OFFSET 0x04 140 141/* 142 * WinSystems WS16C48 143 * 144 * Offset Page 0 Page 1 Page 2 Page 3 145 * ------ ----------- ----------- ----------- ----------- 146 * 0x10 Port 0 I/O Port 0 I/O Port 0 I/O Port 0 I/O 147 * 0x11 Port 1 I/O Port 1 I/O Port 1 I/O Port 1 I/O 148 * 0x12 Port 2 I/O Port 2 I/O Port 2 I/O Port 2 I/O 149 * 0x13 Port 3 I/O Port 3 I/O Port 3 I/O Port 3 I/O 150 * 0x14 Port 4 I/O Port 4 I/O Port 4 I/O Port 4 I/O 151 * 0x15 Port 5 I/O Port 5 I/O Port 5 I/O Port 5 I/O 152 * 0x16 INT_PENDING INT_PENDING INT_PENDING INT_PENDING 153 * 0x17 Page/Lock Page/Lock Page/Lock Page/Lock 154 * 0x18 N/A POL_0 ENAB_0 INT_ID0 155 * 0x19 N/A POL_1 ENAB_1 INT_ID1 156 * 0x1a N/A POL_2 ENAB_2 INT_ID2 157 */ 158#define PCMMIO_PORT_REG(x) (0x10 + (x)) 159#define PCMMIO_INT_PENDING_REG 0x16 160#define PCMMIO_PAGE_LOCK_REG 0x17 161#define PCMMIO_LOCK_PORT(x) ((1 << (x)) & 0x3f) 162#define PCMMIO_PAGE(x) (((x) & 0x3) << 6) 163#define PCMMIO_PAGE_MASK PCMUIO_PAGE(3) 164#define PCMMIO_PAGE_POL 1 165#define PCMMIO_PAGE_ENAB 2 166#define PCMMIO_PAGE_INT_ID 3 167#define PCMMIO_PAGE_REG(x) (0x18 + (x)) 168 169static const struct comedi_lrange pcmmio_ai_ranges = { 170 4, { 171 BIP_RANGE(5), 172 BIP_RANGE(10), 173 UNI_RANGE(5), 174 UNI_RANGE(10) 175 } 176}; 177 178static const struct comedi_lrange pcmmio_ao_ranges = { 179 6, { 180 UNI_RANGE(5), 181 UNI_RANGE(10), 182 BIP_RANGE(5), 183 BIP_RANGE(10), 184 BIP_RANGE(2.5), 185 RANGE(-2.5, 7.5) 186 } 187}; 188 189struct pcmmio_private { 190 spinlock_t pagelock; /* protects the page registers */ 191 spinlock_t spinlock; /* protects the member variables */ 192 unsigned int enabled_mask; 193 unsigned int stop_count; 194 unsigned int active:1; 195}; 196 197static void pcmmio_dio_write(struct comedi_device *dev, unsigned int val, 198 int page, int port) 199{ 200 struct pcmmio_private *devpriv = dev->private; 201 unsigned long iobase = dev->iobase; 202 unsigned long flags; 203 204 spin_lock_irqsave(&devpriv->pagelock, flags); 205 if (page == 0) { 206 /* Port registers are valid for any page */ 207 outb(val & 0xff, iobase + PCMMIO_PORT_REG(port + 0)); 208 outb((val >> 8) & 0xff, iobase + PCMMIO_PORT_REG(port + 1)); 209 outb((val >> 16) & 0xff, iobase + PCMMIO_PORT_REG(port + 2)); 210 } else { 211 outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG); 212 outb(val & 0xff, iobase + PCMMIO_PAGE_REG(0)); 213 outb((val >> 8) & 0xff, iobase + PCMMIO_PAGE_REG(1)); 214 outb((val >> 16) & 0xff, iobase + PCMMIO_PAGE_REG(2)); 215 } 216 spin_unlock_irqrestore(&devpriv->pagelock, flags); 217} 218 219static unsigned int pcmmio_dio_read(struct comedi_device *dev, 220 int page, int port) 221{ 222 struct pcmmio_private *devpriv = dev->private; 223 unsigned long iobase = dev->iobase; 224 unsigned long flags; 225 unsigned int val; 226 227 spin_lock_irqsave(&devpriv->pagelock, flags); 228 if (page == 0) { 229 /* Port registers are valid for any page */ 230 val = inb(iobase + PCMMIO_PORT_REG(port + 0)); 231 val |= (inb(iobase + PCMMIO_PORT_REG(port + 1)) << 8); 232 val |= (inb(iobase + PCMMIO_PORT_REG(port + 2)) << 16); 233 } else { 234 outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG); 235 val = inb(iobase + PCMMIO_PAGE_REG(0)); 236 val |= (inb(iobase + PCMMIO_PAGE_REG(1)) << 8); 237 val |= (inb(iobase + PCMMIO_PAGE_REG(2)) << 16); 238 } 239 spin_unlock_irqrestore(&devpriv->pagelock, flags); 240 241 return val; 242} 243 244/* 245 * Each channel can be individually programmed for input or output. 246 * Writing a '0' to a channel causes the corresponding output pin 247 * to go to a high-z state (pulled high by an external 10K resistor). 248 * This allows it to be used as an input. When used in the input mode, 249 * a read reflects the inverted state of the I/O pin, such that a 250 * high on the pin will read as a '0' in the register. Writing a '1' 251 * to a bit position causes the pin to sink current (up to 12mA), 252 * effectively pulling it low. 253 */ 254static int pcmmio_dio_insn_bits(struct comedi_device *dev, 255 struct comedi_subdevice *s, 256 struct comedi_insn *insn, 257 unsigned int *data) 258{ 259 /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */ 260 int port = s->index == 2 ? 0 : 3; 261 unsigned int chanmask = (1 << s->n_chan) - 1; 262 unsigned int mask; 263 unsigned int val; 264 265 mask = comedi_dio_update_state(s, data); 266 if (mask) { 267 /* 268 * Outputs are inverted, invert the state and 269 * update the channels. 270 * 271 * The s->io_bits mask makes sure the input channels 272 * are '0' so that the outputs pins stay in a high 273 * z-state. 274 */ 275 val = ~s->state & chanmask; 276 val &= s->io_bits; 277 pcmmio_dio_write(dev, val, 0, port); 278 } 279 280 /* get inverted state of the channels from the port */ 281 val = pcmmio_dio_read(dev, 0, port); 282 283 /* return the true state of the channels */ 284 data[1] = ~val & chanmask; 285 286 return insn->n; 287} 288 289static int pcmmio_dio_insn_config(struct comedi_device *dev, 290 struct comedi_subdevice *s, 291 struct comedi_insn *insn, 292 unsigned int *data) 293{ 294 /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */ 295 int port = s->index == 2 ? 0 : 3; 296 int ret; 297 298 ret = comedi_dio_insn_config(dev, s, insn, data, 0); 299 if (ret) 300 return ret; 301 302 if (data[0] == INSN_CONFIG_DIO_INPUT) 303 pcmmio_dio_write(dev, s->io_bits, 0, port); 304 305 return insn->n; 306} 307 308static void pcmmio_reset(struct comedi_device *dev) 309{ 310 /* Clear all the DIO port bits */ 311 pcmmio_dio_write(dev, 0, 0, 0); 312 pcmmio_dio_write(dev, 0, 0, 3); 313 314 /* Clear all the paged registers */ 315 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_POL, 0); 316 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0); 317 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0); 318} 319 320/* devpriv->spinlock is already locked */ 321static void pcmmio_stop_intr(struct comedi_device *dev, 322 struct comedi_subdevice *s) 323{ 324 struct pcmmio_private *devpriv = dev->private; 325 326 devpriv->enabled_mask = 0; 327 devpriv->active = 0; 328 s->async->inttrig = NULL; 329 330 /* disable all dio interrupts */ 331 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0); 332} 333 334static void pcmmio_handle_dio_intr(struct comedi_device *dev, 335 struct comedi_subdevice *s, 336 unsigned int triggered) 337{ 338 struct pcmmio_private *devpriv = dev->private; 339 struct comedi_cmd *cmd = &s->async->cmd; 340 unsigned int oldevents = s->async->events; 341 unsigned int val = 0; 342 unsigned long flags; 343 int i; 344 345 spin_lock_irqsave(&devpriv->spinlock, flags); 346 347 if (!devpriv->active) 348 goto done; 349 350 if (!(triggered & devpriv->enabled_mask)) 351 goto done; 352 353 for (i = 0; i < cmd->chanlist_len; i++) { 354 unsigned int chan = CR_CHAN(cmd->chanlist[i]); 355 356 if (triggered & (1 << chan)) 357 val |= (1 << i); 358 } 359 360 /* Write the scan to the buffer. */ 361 if (comedi_buf_put(s, val) && 362 comedi_buf_put(s, val >> 16)) { 363 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS); 364 } else { 365 /* Overflow! Stop acquisition!! */ 366 /* TODO: STOP_ACQUISITION_CALL_HERE!! */ 367 pcmmio_stop_intr(dev, s); 368 } 369 370 /* Check for end of acquisition. */ 371 if (cmd->stop_src == TRIG_COUNT && devpriv->stop_count > 0) { 372 devpriv->stop_count--; 373 if (devpriv->stop_count == 0) { 374 s->async->events |= COMEDI_CB_EOA; 375 /* TODO: STOP_ACQUISITION_CALL_HERE!! */ 376 pcmmio_stop_intr(dev, s); 377 } 378 } 379 380done: 381 spin_unlock_irqrestore(&devpriv->spinlock, flags); 382 383 if (oldevents != s->async->events) 384 comedi_event(dev, s); 385} 386 387static irqreturn_t interrupt_pcmmio(int irq, void *d) 388{ 389 struct comedi_device *dev = d; 390 struct comedi_subdevice *s = dev->read_subdev; 391 unsigned int triggered; 392 unsigned char int_pend; 393 394 /* are there any interrupts pending */ 395 int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07; 396 if (!int_pend) 397 return IRQ_NONE; 398 399 /* get, and clear, the pending interrupts */ 400 triggered = pcmmio_dio_read(dev, PCMMIO_PAGE_INT_ID, 0); 401 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0); 402 403 pcmmio_handle_dio_intr(dev, s, triggered); 404 405 return IRQ_HANDLED; 406} 407 408/* devpriv->spinlock is already locked */ 409static void pcmmio_start_intr(struct comedi_device *dev, 410 struct comedi_subdevice *s) 411{ 412 struct pcmmio_private *devpriv = dev->private; 413 struct comedi_cmd *cmd = &s->async->cmd; 414 unsigned int bits = 0; 415 unsigned int pol_bits = 0; 416 int i; 417 418 devpriv->enabled_mask = 0; 419 devpriv->active = 1; 420 if (cmd->chanlist) { 421 for (i = 0; i < cmd->chanlist_len; i++) { 422 unsigned int chanspec = cmd->chanlist[i]; 423 unsigned int chan = CR_CHAN(chanspec); 424 unsigned int range = CR_RANGE(chanspec); 425 unsigned int aref = CR_AREF(chanspec); 426 427 bits |= (1 << chan); 428 pol_bits |= (((aref || range) ? 1 : 0) << chan); 429 } 430 } 431 bits &= ((1 << s->n_chan) - 1); 432 devpriv->enabled_mask = bits; 433 434 /* set polarity and enable interrupts */ 435 pcmmio_dio_write(dev, pol_bits, PCMMIO_PAGE_POL, 0); 436 pcmmio_dio_write(dev, bits, PCMMIO_PAGE_ENAB, 0); 437} 438 439static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s) 440{ 441 struct pcmmio_private *devpriv = dev->private; 442 unsigned long flags; 443 444 spin_lock_irqsave(&devpriv->spinlock, flags); 445 if (devpriv->active) 446 pcmmio_stop_intr(dev, s); 447 spin_unlock_irqrestore(&devpriv->spinlock, flags); 448 449 return 0; 450} 451 452static int pcmmio_inttrig_start_intr(struct comedi_device *dev, 453 struct comedi_subdevice *s, 454 unsigned int trig_num) 455{ 456 struct pcmmio_private *devpriv = dev->private; 457 struct comedi_cmd *cmd = &s->async->cmd; 458 unsigned long flags; 459 460 if (trig_num != cmd->start_arg) 461 return -EINVAL; 462 463 spin_lock_irqsave(&devpriv->spinlock, flags); 464 s->async->inttrig = NULL; 465 if (devpriv->active) 466 pcmmio_start_intr(dev, s); 467 spin_unlock_irqrestore(&devpriv->spinlock, flags); 468 469 return 1; 470} 471 472/* 473 * 'do_cmd' function for an 'INTERRUPT' subdevice. 474 */ 475static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 476{ 477 struct pcmmio_private *devpriv = dev->private; 478 struct comedi_cmd *cmd = &s->async->cmd; 479 unsigned long flags; 480 481 spin_lock_irqsave(&devpriv->spinlock, flags); 482 devpriv->active = 1; 483 484 devpriv->stop_count = cmd->stop_arg; 485 486 /* Set up start of acquisition. */ 487 if (cmd->start_src == TRIG_INT) 488 s->async->inttrig = pcmmio_inttrig_start_intr; 489 else /* TRIG_NOW */ 490 pcmmio_start_intr(dev, s); 491 492 spin_unlock_irqrestore(&devpriv->spinlock, flags); 493 494 return 0; 495} 496 497static int pcmmio_cmdtest(struct comedi_device *dev, 498 struct comedi_subdevice *s, 499 struct comedi_cmd *cmd) 500{ 501 int err = 0; 502 503 /* Step 1 : check if triggers are trivially valid */ 504 505 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT); 506 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); 507 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW); 508 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 509 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 510 511 if (err) 512 return 1; 513 514 /* Step 2a : make sure trigger sources are unique */ 515 516 err |= cfc_check_trigger_is_unique(cmd->start_src); 517 err |= cfc_check_trigger_is_unique(cmd->stop_src); 518 519 /* Step 2b : and mutually compatible */ 520 521 if (err) 522 return 2; 523 524 /* Step 3: check if arguments are trivially valid */ 525 526 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); 527 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 528 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0); 529 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); 530 531 if (cmd->stop_src == TRIG_COUNT) 532 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1); 533 else /* TRIG_NONE */ 534 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); 535 536 if (err) 537 return 3; 538 539 /* step 4: fix up any arguments */ 540 541 /* if (err) return 4; */ 542 543 return 0; 544} 545 546static int pcmmio_ai_eoc(struct comedi_device *dev, 547 struct comedi_subdevice *s, 548 struct comedi_insn *insn, 549 unsigned long context) 550{ 551 unsigned char status; 552 553 status = inb(dev->iobase + PCMMIO_AI_STATUS_REG); 554 if (status & PCMMIO_AI_STATUS_DATA_READY) 555 return 0; 556 return -EBUSY; 557} 558 559static int pcmmio_ai_insn_read(struct comedi_device *dev, 560 struct comedi_subdevice *s, 561 struct comedi_insn *insn, 562 unsigned int *data) 563{ 564 unsigned long iobase = dev->iobase; 565 unsigned int chan = CR_CHAN(insn->chanspec); 566 unsigned int range = CR_RANGE(insn->chanspec); 567 unsigned int aref = CR_AREF(insn->chanspec); 568 unsigned char cmd = 0; 569 unsigned int val; 570 int ret; 571 int i; 572 573 /* 574 * The PCM-MIO uses two Linear Tech LTC1859CG 8-channel A/D converters. 575 * The devices use a full duplex serial interface which transmits and 576 * receives data simultaneously. An 8-bit command is shifted into the 577 * ADC interface to configure it for the next conversion. At the same 578 * time, the data from the previous conversion is shifted out of the 579 * device. Consequently, the conversion result is delayed by one 580 * conversion from the command word. 581 * 582 * Setup the cmd for the conversions then do a dummy conversion to 583 * flush the junk data. Then do each conversion requested by the 584 * comedi_insn. Note that the last conversion will leave junk data 585 * in ADC which will get flushed on the next comedi_insn. 586 */ 587 588 if (chan > 7) { 589 chan -= 8; 590 iobase += PCMMIO_AI_2ND_ADC_OFFSET; 591 } 592 593 if (aref == AREF_GROUND) 594 cmd |= PCMMIO_AI_CMD_SE; 595 if (chan % 2) 596 cmd |= PCMMIO_AI_CMD_ODD_CHAN; 597 cmd |= PCMMIO_AI_CMD_CHAN_SEL(chan / 2); 598 cmd |= PCMMIO_AI_CMD_RANGE(range); 599 600 outb(cmd, iobase + PCMMIO_AI_CMD_REG); 601 602 ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0); 603 if (ret) 604 return ret; 605 606 val = inb(iobase + PCMMIO_AI_LSB_REG); 607 val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8; 608 609 for (i = 0; i < insn->n; i++) { 610 outb(cmd, iobase + PCMMIO_AI_CMD_REG); 611 612 ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0); 613 if (ret) 614 return ret; 615 616 val = inb(iobase + PCMMIO_AI_LSB_REG); 617 val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8; 618 619 /* bipolar data is two's complement */ 620 if (comedi_range_is_bipolar(s, range)) 621 val = comedi_offset_munge(s, val); 622 623 data[i] = val; 624 } 625 626 return insn->n; 627} 628 629static int pcmmio_ao_eoc(struct comedi_device *dev, 630 struct comedi_subdevice *s, 631 struct comedi_insn *insn, 632 unsigned long context) 633{ 634 unsigned char status; 635 636 status = inb(dev->iobase + PCMMIO_AO_STATUS_REG); 637 if (status & PCMMIO_AO_STATUS_DATA_READY) 638 return 0; 639 return -EBUSY; 640} 641 642static int pcmmio_ao_insn_write(struct comedi_device *dev, 643 struct comedi_subdevice *s, 644 struct comedi_insn *insn, 645 unsigned int *data) 646{ 647 unsigned long iobase = dev->iobase; 648 unsigned int chan = CR_CHAN(insn->chanspec); 649 unsigned int range = CR_RANGE(insn->chanspec); 650 unsigned char cmd = 0; 651 int ret; 652 int i; 653 654 /* 655 * The PCM-MIO has two Linear Tech LTC2704 DAC devices. Each device 656 * is a 4-channel converter with software-selectable output range. 657 */ 658 659 if (chan > 3) { 660 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan - 4); 661 iobase += PCMMIO_AO_2ND_DAC_OFFSET; 662 } else { 663 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan); 664 } 665 666 /* set the range for the channel */ 667 outb(PCMMIO_AO_LSB_SPAN(range), iobase + PCMMIO_AO_LSB_REG); 668 outb(0, iobase + PCMMIO_AO_MSB_REG); 669 outb(cmd | PCMMIO_AO_CMD_WR_SPAN_UPDATE, iobase + PCMMIO_AO_CMD_REG); 670 671 ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0); 672 if (ret) 673 return ret; 674 675 for (i = 0; i < insn->n; i++) { 676 unsigned int val = data[i]; 677 678 /* write the data to the channel */ 679 outb(val & 0xff, iobase + PCMMIO_AO_LSB_REG); 680 outb((val >> 8) & 0xff, iobase + PCMMIO_AO_MSB_REG); 681 outb(cmd | PCMMIO_AO_CMD_WR_CODE_UPDATE, 682 iobase + PCMMIO_AO_CMD_REG); 683 684 ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0); 685 if (ret) 686 return ret; 687 688 s->readback[chan] = val; 689 } 690 691 return insn->n; 692} 693 694static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it) 695{ 696 struct pcmmio_private *devpriv; 697 struct comedi_subdevice *s; 698 int ret; 699 700 ret = comedi_request_region(dev, it->options[0], 32); 701 if (ret) 702 return ret; 703 704 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 705 if (!devpriv) 706 return -ENOMEM; 707 708 spin_lock_init(&devpriv->pagelock); 709 spin_lock_init(&devpriv->spinlock); 710 711 pcmmio_reset(dev); 712 713 if (it->options[1]) { 714 ret = request_irq(it->options[1], interrupt_pcmmio, 0, 715 dev->board_name, dev); 716 if (ret == 0) { 717 dev->irq = it->options[1]; 718 719 /* configure the interrupt routing on the board */ 720 outb(PCMMIO_AI_RES_ENA_DIO_RES_ACCESS, 721 dev->iobase + PCMMIO_AI_RES_ENA_REG); 722 outb(PCMMIO_RESOURCE_IRQ(dev->irq), 723 dev->iobase + PCMMIO_RESOURCE_REG); 724 } 725 } 726 727 ret = comedi_alloc_subdevices(dev, 4); 728 if (ret) 729 return ret; 730 731 /* Analog Input subdevice */ 732 s = &dev->subdevices[0]; 733 s->type = COMEDI_SUBD_AI; 734 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; 735 s->n_chan = 16; 736 s->maxdata = 0xffff; 737 s->range_table = &pcmmio_ai_ranges; 738 s->insn_read = pcmmio_ai_insn_read; 739 740 /* initialize the resource enable register by clearing it */ 741 outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS, 742 dev->iobase + PCMMIO_AI_RES_ENA_REG); 743 outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS, 744 dev->iobase + PCMMIO_AI_RES_ENA_REG + PCMMIO_AI_2ND_ADC_OFFSET); 745 746 /* Analog Output subdevice */ 747 s = &dev->subdevices[1]; 748 s->type = COMEDI_SUBD_AO; 749 s->subdev_flags = SDF_READABLE; 750 s->n_chan = 8; 751 s->maxdata = 0xffff; 752 s->range_table = &pcmmio_ao_ranges; 753 s->insn_write = pcmmio_ao_insn_write; 754 s->insn_read = comedi_readback_insn_read; 755 756 ret = comedi_alloc_subdev_readback(s); 757 if (ret) 758 return ret; 759 760 /* initialize the resource enable register by clearing it */ 761 outb(0, dev->iobase + PCMMIO_AO_RESOURCE_ENA_REG); 762 outb(0, dev->iobase + PCMMIO_AO_2ND_DAC_OFFSET + 763 PCMMIO_AO_RESOURCE_ENA_REG); 764 765 /* Digital I/O subdevice with interrupt support */ 766 s = &dev->subdevices[2]; 767 s->type = COMEDI_SUBD_DIO; 768 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 769 s->n_chan = 24; 770 s->maxdata = 1; 771 s->len_chanlist = 1; 772 s->range_table = &range_digital; 773 s->insn_bits = pcmmio_dio_insn_bits; 774 s->insn_config = pcmmio_dio_insn_config; 775 if (dev->irq) { 776 dev->read_subdev = s; 777 s->subdev_flags |= SDF_CMD_READ; 778 s->len_chanlist = s->n_chan; 779 s->cancel = pcmmio_cancel; 780 s->do_cmd = pcmmio_cmd; 781 s->do_cmdtest = pcmmio_cmdtest; 782 } 783 784 /* Digital I/O subdevice */ 785 s = &dev->subdevices[3]; 786 s->type = COMEDI_SUBD_DIO; 787 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 788 s->n_chan = 24; 789 s->maxdata = 1; 790 s->range_table = &range_digital; 791 s->insn_bits = pcmmio_dio_insn_bits; 792 s->insn_config = pcmmio_dio_insn_config; 793 794 return 0; 795} 796 797static struct comedi_driver pcmmio_driver = { 798 .driver_name = "pcmmio", 799 .module = THIS_MODULE, 800 .attach = pcmmio_attach, 801 .detach = comedi_legacy_detach, 802}; 803module_comedi_driver(pcmmio_driver); 804 805MODULE_AUTHOR("Comedi http://www.comedi.org"); 806MODULE_DESCRIPTION("Comedi driver for Winsystems PCM-MIO PC/104 board"); 807MODULE_LICENSE("GPL"); 808