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