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