cb_pcimdas.c (12780B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * comedi/drivers/cb_pcimdas.c 4 * Comedi driver for Computer Boards PCIM-DAS1602/16 and PCIe-DAS1602/16 5 * 6 * COMEDI - Linux Control and Measurement Device Interface 7 * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 8 */ 9 10/* 11 * Driver: cb_pcimdas 12 * Description: Measurement Computing PCI Migration series boards 13 * Devices: [ComputerBoards] PCIM-DAS1602/16 (cb_pcimdas), PCIe-DAS1602/16 14 * Author: Richard Bytheway 15 * Updated: Mon, 13 Oct 2014 11:57:39 +0000 16 * Status: experimental 17 * 18 * Written to support the PCIM-DAS1602/16 and PCIe-DAS1602/16. 19 * 20 * Configuration Options: 21 * none 22 * 23 * Manual configuration of PCI(e) cards is not supported; they are configured 24 * automatically. 25 * 26 * Developed from cb_pcidas and skel by Richard Bytheway (mocelet@sucs.org). 27 * Only supports DIO, AO and simple AI in it's present form. 28 * No interrupts, multi channel or FIFO AI, 29 * although the card looks like it could support this. 30 * 31 * https://www.mccdaq.com/PDFs/Manuals/pcim-das1602-16.pdf 32 * https://www.mccdaq.com/PDFs/Manuals/pcie-das1602-16.pdf 33 */ 34 35#include <linux/module.h> 36#include <linux/interrupt.h> 37#include <linux/comedi/comedi_pci.h> 38#include <linux/comedi/comedi_8255.h> 39#include <linux/comedi/comedi_8254.h> 40 41#include "plx9052.h" 42 43/* 44 * PCI Bar 1 Register map 45 * see plx9052.h for register and bit defines 46 */ 47 48/* 49 * PCI Bar 2 Register map (devpriv->daqio) 50 */ 51#define PCIMDAS_AI_REG 0x00 52#define PCIMDAS_AI_SOFTTRIG_REG 0x00 53#define PCIMDAS_AO_REG(x) (0x02 + ((x) * 2)) 54 55/* 56 * PCI Bar 3 Register map (devpriv->BADR3) 57 */ 58#define PCIMDAS_MUX_REG 0x00 59#define PCIMDAS_MUX(_lo, _hi) ((_lo) | ((_hi) << 4)) 60#define PCIMDAS_DI_DO_REG 0x01 61#define PCIMDAS_STATUS_REG 0x02 62#define PCIMDAS_STATUS_EOC BIT(7) 63#define PCIMDAS_STATUS_UB BIT(6) 64#define PCIMDAS_STATUS_MUX BIT(5) 65#define PCIMDAS_STATUS_CLK BIT(4) 66#define PCIMDAS_STATUS_TO_CURR_MUX(x) ((x) & 0xf) 67#define PCIMDAS_CONV_STATUS_REG 0x03 68#define PCIMDAS_CONV_STATUS_EOC BIT(7) 69#define PCIMDAS_CONV_STATUS_EOB BIT(6) 70#define PCIMDAS_CONV_STATUS_EOA BIT(5) 71#define PCIMDAS_CONV_STATUS_FNE BIT(4) 72#define PCIMDAS_CONV_STATUS_FHF BIT(3) 73#define PCIMDAS_CONV_STATUS_OVERRUN BIT(2) 74#define PCIMDAS_IRQ_REG 0x04 75#define PCIMDAS_IRQ_INTE BIT(7) 76#define PCIMDAS_IRQ_INT BIT(6) 77#define PCIMDAS_IRQ_OVERRUN BIT(4) 78#define PCIMDAS_IRQ_EOA BIT(3) 79#define PCIMDAS_IRQ_EOA_INT_SEL BIT(2) 80#define PCIMDAS_IRQ_INTSEL(x) ((x) << 0) 81#define PCIMDAS_IRQ_INTSEL_EOC PCIMDAS_IRQ_INTSEL(0) 82#define PCIMDAS_IRQ_INTSEL_FNE PCIMDAS_IRQ_INTSEL(1) 83#define PCIMDAS_IRQ_INTSEL_EOB PCIMDAS_IRQ_INTSEL(2) 84#define PCIMDAS_IRQ_INTSEL_FHF_EOA PCIMDAS_IRQ_INTSEL(3) 85#define PCIMDAS_PACER_REG 0x05 86#define PCIMDAS_PACER_GATE_STATUS BIT(6) 87#define PCIMDAS_PACER_GATE_POL BIT(5) 88#define PCIMDAS_PACER_GATE_LATCH BIT(4) 89#define PCIMDAS_PACER_GATE_EN BIT(3) 90#define PCIMDAS_PACER_EXT_PACER_POL BIT(2) 91#define PCIMDAS_PACER_SRC(x) ((x) << 0) 92#define PCIMDAS_PACER_SRC_POLLED PCIMDAS_PACER_SRC(0) 93#define PCIMDAS_PACER_SRC_EXT PCIMDAS_PACER_SRC(2) 94#define PCIMDAS_PACER_SRC_INT PCIMDAS_PACER_SRC(3) 95#define PCIMDAS_PACER_SRC_MASK (3 << 0) 96#define PCIMDAS_BURST_REG 0x06 97#define PCIMDAS_BURST_BME BIT(1) 98#define PCIMDAS_BURST_CONV_EN BIT(0) 99#define PCIMDAS_GAIN_REG 0x07 100#define PCIMDAS_8254_BASE 0x08 101#define PCIMDAS_USER_CNTR_REG 0x0c 102#define PCIMDAS_USER_CNTR_CTR1_CLK_SEL BIT(0) 103#define PCIMDAS_RESIDUE_MSB_REG 0x0d 104#define PCIMDAS_RESIDUE_LSB_REG 0x0e 105 106/* 107 * PCI Bar 4 Register map (dev->iobase) 108 */ 109#define PCIMDAS_8255_BASE 0x00 110 111static const struct comedi_lrange cb_pcimdas_ai_bip_range = { 112 4, { 113 BIP_RANGE(10), 114 BIP_RANGE(5), 115 BIP_RANGE(2.5), 116 BIP_RANGE(1.25) 117 } 118}; 119 120static const struct comedi_lrange cb_pcimdas_ai_uni_range = { 121 4, { 122 UNI_RANGE(10), 123 UNI_RANGE(5), 124 UNI_RANGE(2.5), 125 UNI_RANGE(1.25) 126 } 127}; 128 129/* 130 * The Analog Output range is not programmable. The DAC ranges are 131 * jumper-settable on the board. The settings are not software-readable. 132 */ 133static const struct comedi_lrange cb_pcimdas_ao_range = { 134 6, { 135 BIP_RANGE(10), 136 BIP_RANGE(5), 137 UNI_RANGE(10), 138 UNI_RANGE(5), 139 RANGE_ext(-1, 1), 140 RANGE_ext(0, 1) 141 } 142}; 143 144/* 145 * this structure is for data unique to this hardware driver. If 146 * several hardware drivers keep similar information in this structure, 147 * feel free to suggest moving the variable to the struct comedi_device 148 * struct. 149 */ 150struct cb_pcimdas_private { 151 /* base addresses */ 152 unsigned long daqio; 153 unsigned long BADR3; 154}; 155 156static int cb_pcimdas_ai_eoc(struct comedi_device *dev, 157 struct comedi_subdevice *s, 158 struct comedi_insn *insn, 159 unsigned long context) 160{ 161 struct cb_pcimdas_private *devpriv = dev->private; 162 unsigned int status; 163 164 status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG); 165 if ((status & PCIMDAS_STATUS_EOC) == 0) 166 return 0; 167 return -EBUSY; 168} 169 170static int cb_pcimdas_ai_insn_read(struct comedi_device *dev, 171 struct comedi_subdevice *s, 172 struct comedi_insn *insn, 173 unsigned int *data) 174{ 175 struct cb_pcimdas_private *devpriv = dev->private; 176 unsigned int chan = CR_CHAN(insn->chanspec); 177 unsigned int range = CR_RANGE(insn->chanspec); 178 int n; 179 unsigned int d; 180 int ret; 181 182 /* only support sw initiated reads from a single channel */ 183 184 /* configure for sw initiated read */ 185 d = inb(devpriv->BADR3 + PCIMDAS_PACER_REG); 186 if ((d & PCIMDAS_PACER_SRC_MASK) != PCIMDAS_PACER_SRC_POLLED) { 187 d &= ~PCIMDAS_PACER_SRC_MASK; 188 d |= PCIMDAS_PACER_SRC_POLLED; 189 outb(d, devpriv->BADR3 + PCIMDAS_PACER_REG); 190 } 191 192 /* set bursting off, conversions on */ 193 outb(PCIMDAS_BURST_CONV_EN, devpriv->BADR3 + PCIMDAS_BURST_REG); 194 195 /* set range */ 196 outb(range, devpriv->BADR3 + PCIMDAS_GAIN_REG); 197 198 /* set mux for single channel scan */ 199 outb(PCIMDAS_MUX(chan, chan), devpriv->BADR3 + PCIMDAS_MUX_REG); 200 201 /* convert n samples */ 202 for (n = 0; n < insn->n; n++) { 203 /* trigger conversion */ 204 outw(0, devpriv->daqio + PCIMDAS_AI_SOFTTRIG_REG); 205 206 /* wait for conversion to end */ 207 ret = comedi_timeout(dev, s, insn, cb_pcimdas_ai_eoc, 0); 208 if (ret) 209 return ret; 210 211 /* read data */ 212 data[n] = inw(devpriv->daqio + PCIMDAS_AI_REG); 213 } 214 215 /* return the number of samples read/written */ 216 return n; 217} 218 219static int cb_pcimdas_ao_insn_write(struct comedi_device *dev, 220 struct comedi_subdevice *s, 221 struct comedi_insn *insn, 222 unsigned int *data) 223{ 224 struct cb_pcimdas_private *devpriv = dev->private; 225 unsigned int chan = CR_CHAN(insn->chanspec); 226 unsigned int val = s->readback[chan]; 227 int i; 228 229 for (i = 0; i < insn->n; i++) { 230 val = data[i]; 231 outw(val, devpriv->daqio + PCIMDAS_AO_REG(chan)); 232 } 233 s->readback[chan] = val; 234 235 return insn->n; 236} 237 238static int cb_pcimdas_di_insn_bits(struct comedi_device *dev, 239 struct comedi_subdevice *s, 240 struct comedi_insn *insn, 241 unsigned int *data) 242{ 243 struct cb_pcimdas_private *devpriv = dev->private; 244 unsigned int val; 245 246 val = inb(devpriv->BADR3 + PCIMDAS_DI_DO_REG); 247 248 data[1] = val & 0x0f; 249 250 return insn->n; 251} 252 253static int cb_pcimdas_do_insn_bits(struct comedi_device *dev, 254 struct comedi_subdevice *s, 255 struct comedi_insn *insn, 256 unsigned int *data) 257{ 258 struct cb_pcimdas_private *devpriv = dev->private; 259 260 if (comedi_dio_update_state(s, data)) 261 outb(s->state, devpriv->BADR3 + PCIMDAS_DI_DO_REG); 262 263 data[1] = s->state; 264 265 return insn->n; 266} 267 268static int cb_pcimdas_counter_insn_config(struct comedi_device *dev, 269 struct comedi_subdevice *s, 270 struct comedi_insn *insn, 271 unsigned int *data) 272{ 273 struct cb_pcimdas_private *devpriv = dev->private; 274 unsigned int ctrl; 275 276 switch (data[0]) { 277 case INSN_CONFIG_SET_CLOCK_SRC: 278 switch (data[1]) { 279 case 0: /* internal 100 kHz clock */ 280 ctrl = PCIMDAS_USER_CNTR_CTR1_CLK_SEL; 281 break; 282 case 1: /* external clk on pin 21 */ 283 ctrl = 0; 284 break; 285 default: 286 return -EINVAL; 287 } 288 outb(ctrl, devpriv->BADR3 + PCIMDAS_USER_CNTR_REG); 289 break; 290 case INSN_CONFIG_GET_CLOCK_SRC: 291 ctrl = inb(devpriv->BADR3 + PCIMDAS_USER_CNTR_REG); 292 if (ctrl & PCIMDAS_USER_CNTR_CTR1_CLK_SEL) { 293 data[1] = 0; 294 data[2] = I8254_OSC_BASE_100KHZ; 295 } else { 296 data[1] = 1; 297 data[2] = 0; 298 } 299 break; 300 default: 301 return -EINVAL; 302 } 303 304 return insn->n; 305} 306 307static unsigned int cb_pcimdas_pacer_clk(struct comedi_device *dev) 308{ 309 struct cb_pcimdas_private *devpriv = dev->private; 310 unsigned int status; 311 312 /* The Pacer Clock jumper selects a 10 MHz or 1 MHz clock */ 313 status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG); 314 if (status & PCIMDAS_STATUS_CLK) 315 return I8254_OSC_BASE_10MHZ; 316 return I8254_OSC_BASE_1MHZ; 317} 318 319static bool cb_pcimdas_is_ai_se(struct comedi_device *dev) 320{ 321 struct cb_pcimdas_private *devpriv = dev->private; 322 unsigned int status; 323 324 /* 325 * The number of Analog Input channels is set with the 326 * Analog Input Mode Switch on the board. The board can 327 * have 16 single-ended or 8 differential channels. 328 */ 329 status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG); 330 return status & PCIMDAS_STATUS_MUX; 331} 332 333static bool cb_pcimdas_is_ai_uni(struct comedi_device *dev) 334{ 335 struct cb_pcimdas_private *devpriv = dev->private; 336 unsigned int status; 337 338 /* 339 * The Analog Input range polarity is set with the 340 * Analog Input Polarity Switch on the board. The 341 * inputs can be set to Unipolar or Bipolar ranges. 342 */ 343 status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG); 344 return status & PCIMDAS_STATUS_UB; 345} 346 347static int cb_pcimdas_auto_attach(struct comedi_device *dev, 348 unsigned long context_unused) 349{ 350 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 351 struct cb_pcimdas_private *devpriv; 352 struct comedi_subdevice *s; 353 int ret; 354 355 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 356 if (!devpriv) 357 return -ENOMEM; 358 359 ret = comedi_pci_enable(dev); 360 if (ret) 361 return ret; 362 363 devpriv->daqio = pci_resource_start(pcidev, 2); 364 devpriv->BADR3 = pci_resource_start(pcidev, 3); 365 dev->iobase = pci_resource_start(pcidev, 4); 366 367 dev->pacer = comedi_8254_init(devpriv->BADR3 + PCIMDAS_8254_BASE, 368 cb_pcimdas_pacer_clk(dev), 369 I8254_IO8, 0); 370 if (!dev->pacer) 371 return -ENOMEM; 372 373 ret = comedi_alloc_subdevices(dev, 6); 374 if (ret) 375 return ret; 376 377 /* Analog Input subdevice */ 378 s = &dev->subdevices[0]; 379 s->type = COMEDI_SUBD_AI; 380 s->subdev_flags = SDF_READABLE; 381 if (cb_pcimdas_is_ai_se(dev)) { 382 s->subdev_flags |= SDF_GROUND; 383 s->n_chan = 16; 384 } else { 385 s->subdev_flags |= SDF_DIFF; 386 s->n_chan = 8; 387 } 388 s->maxdata = 0xffff; 389 s->range_table = cb_pcimdas_is_ai_uni(dev) ? &cb_pcimdas_ai_uni_range 390 : &cb_pcimdas_ai_bip_range; 391 s->insn_read = cb_pcimdas_ai_insn_read; 392 393 /* Analog Output subdevice */ 394 s = &dev->subdevices[1]; 395 s->type = COMEDI_SUBD_AO; 396 s->subdev_flags = SDF_WRITABLE; 397 s->n_chan = 2; 398 s->maxdata = 0xfff; 399 s->range_table = &cb_pcimdas_ao_range; 400 s->insn_write = cb_pcimdas_ao_insn_write; 401 402 ret = comedi_alloc_subdev_readback(s); 403 if (ret) 404 return ret; 405 406 /* Digital I/O subdevice */ 407 s = &dev->subdevices[2]; 408 ret = subdev_8255_init(dev, s, NULL, PCIMDAS_8255_BASE); 409 if (ret) 410 return ret; 411 412 /* Digital Input subdevice (main connector) */ 413 s = &dev->subdevices[3]; 414 s->type = COMEDI_SUBD_DI; 415 s->subdev_flags = SDF_READABLE; 416 s->n_chan = 4; 417 s->maxdata = 1; 418 s->range_table = &range_digital; 419 s->insn_bits = cb_pcimdas_di_insn_bits; 420 421 /* Digital Output subdevice (main connector) */ 422 s = &dev->subdevices[4]; 423 s->type = COMEDI_SUBD_DO; 424 s->subdev_flags = SDF_WRITABLE; 425 s->n_chan = 4; 426 s->maxdata = 1; 427 s->range_table = &range_digital; 428 s->insn_bits = cb_pcimdas_do_insn_bits; 429 430 /* Counter subdevice (8254) */ 431 s = &dev->subdevices[5]; 432 comedi_8254_subdevice_init(s, dev->pacer); 433 434 dev->pacer->insn_config = cb_pcimdas_counter_insn_config; 435 436 /* counters 1 and 2 are used internally for the pacer */ 437 comedi_8254_set_busy(dev->pacer, 1, true); 438 comedi_8254_set_busy(dev->pacer, 2, true); 439 440 return 0; 441} 442 443static struct comedi_driver cb_pcimdas_driver = { 444 .driver_name = "cb_pcimdas", 445 .module = THIS_MODULE, 446 .auto_attach = cb_pcimdas_auto_attach, 447 .detach = comedi_pci_detach, 448}; 449 450static int cb_pcimdas_pci_probe(struct pci_dev *dev, 451 const struct pci_device_id *id) 452{ 453 return comedi_pci_auto_config(dev, &cb_pcimdas_driver, 454 id->driver_data); 455} 456 457static const struct pci_device_id cb_pcimdas_pci_table[] = { 458 { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0056) }, /* PCIM-DAS1602/16 */ 459 { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0115) }, /* PCIe-DAS1602/16 */ 460 { 0 } 461}; 462MODULE_DEVICE_TABLE(pci, cb_pcimdas_pci_table); 463 464static struct pci_driver cb_pcimdas_pci_driver = { 465 .name = "cb_pcimdas", 466 .id_table = cb_pcimdas_pci_table, 467 .probe = cb_pcimdas_pci_probe, 468 .remove = comedi_pci_auto_unconfig, 469}; 470module_comedi_pci_driver(cb_pcimdas_driver, cb_pcimdas_pci_driver); 471 472MODULE_AUTHOR("Comedi https://www.comedi.org"); 473MODULE_DESCRIPTION("Comedi driver for PCIM-DAS1602/16 and PCIe-DAS1602/16"); 474MODULE_LICENSE("GPL");