root/drivers/staging/comedi/drivers/cb_pcimdas.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cb_pcimdas_ai_eoc
  2. cb_pcimdas_ai_insn_read
  3. cb_pcimdas_ao_insn_write
  4. cb_pcimdas_di_insn_bits
  5. cb_pcimdas_do_insn_bits
  6. cb_pcimdas_counter_insn_config
  7. cb_pcimdas_pacer_clk
  8. cb_pcimdas_is_ai_se
  9. cb_pcimdas_is_ai_uni
  10. cb_pcimdas_auto_attach
  11. cb_pcimdas_pci_probe

   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  * http://www.mccdaq.com/PDFs/Manuals/pcim-das1602-16.pdf
  32  * http://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 
 112 static 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 
 121 static 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  */
 134 static 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  */
 151 struct cb_pcimdas_private {
 152         /* base addresses */
 153         unsigned long daqio;
 154         unsigned long BADR3;
 155 };
 156 
 157 static 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 
 171 static 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 
 220 static 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 
 239 static 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 
 254 static 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 
 269 static 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 
 308 static 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 
 320 static 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 
 334 static 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 
 348 static 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 
 444 static 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 
 451 static 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 
 458 static 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 };
 463 MODULE_DEVICE_TABLE(pci, cb_pcimdas_pci_table);
 464 
 465 static 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 };
 471 module_comedi_pci_driver(cb_pcimdas_driver, cb_pcimdas_pci_driver);
 472 
 473 MODULE_AUTHOR("Comedi http://www.comedi.org");
 474 MODULE_DESCRIPTION("Comedi driver for PCIM-DAS1602/16 and PCIe-DAS1602/16");
 475 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */