linux/drivers/staging/comedi/drivers/cb_pcimdas.c
<<
>>
Prefs
   1/*
   2 * comedi/drivers/cb_pcimdas.c
   3 * Comedi driver for Computer Boards PCIM-DAS1602/16 and PCIe-DAS1602/16
   4 *
   5 * COMEDI - Linux Control and Measurement Device Interface
   6 * Copyright (C) 2000 David A. Schleef <ds@schleef.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: cb_pcimdas
  21 * Description: Measurement Computing PCI Migration series boards
  22 * Devices: [ComputerBoards] PCIM-DAS1602/16 (cb_pcimdas), PCIe-DAS1602/16
  23 * Author: Richard Bytheway
  24 * Updated: Mon, 13 Oct 2014 11:57:39 +0000
  25 * Status: experimental
  26 *
  27 * Written to support the PCIM-DAS1602/16 and PCIe-DAS1602/16.
  28 *
  29 * Configuration Options:
  30 *   none
  31 *
  32 * Manual configuration of PCI(e) cards is not supported; they are configured
  33 * automatically.
  34 *
  35 * Developed from cb_pcidas and skel by Richard Bytheway (mocelet@sucs.org).
  36 * Only supports DIO, AO and simple AI in it's present form.
  37 * No interrupts, multi channel or FIFO AI,
  38 * although the card looks like it could support this.
  39 *
  40 * http://www.mccdaq.com/PDFs/Manuals/pcim-das1602-16.pdf
  41 * http://www.mccdaq.com/PDFs/Manuals/pcie-das1602-16.pdf
  42 */
  43
  44#include <linux/module.h>
  45#include <linux/pci.h>
  46#include <linux/interrupt.h>
  47
  48#include "../comedidev.h"
  49
  50#include "plx9052.h"
  51#include "8255.h"
  52
  53/* Registers for the PCIM-DAS1602/16 and PCIe-DAS1602/16 */
  54
  55/* DAC Offsets */
  56#define ADC_TRIG 0
  57#define DAC0_OFFSET 2
  58#define DAC1_OFFSET 4
  59
  60/* AI and Counter Constants */
  61#define MUX_LIMITS 0
  62#define MAIN_CONN_DIO 1
  63#define ADC_STAT 2
  64#define ADC_CONV_STAT 3
  65#define ADC_INT 4
  66#define ADC_PACER 5
  67#define BURST_MODE 6
  68#define PROG_GAIN 7
  69#define CLK8254_1_DATA 8
  70#define CLK8254_2_DATA 9
  71#define CLK8254_3_DATA 10
  72#define CLK8254_CONTROL 11
  73#define USER_COUNTER 12
  74#define RESID_COUNT_H 13
  75#define RESID_COUNT_L 14
  76
  77/*
  78 * this structure is for data unique to this hardware driver.  If
  79 * several hardware drivers keep similar information in this structure,
  80 * feel free to suggest moving the variable to the struct comedi_device
  81 * struct.
  82 */
  83struct cb_pcimdas_private {
  84        /* base addresses */
  85        unsigned long daqio;
  86        unsigned long BADR3;
  87};
  88
  89static int cb_pcimdas_ai_eoc(struct comedi_device *dev,
  90                             struct comedi_subdevice *s,
  91                             struct comedi_insn *insn,
  92                             unsigned long context)
  93{
  94        struct cb_pcimdas_private *devpriv = dev->private;
  95        unsigned int status;
  96
  97        status = inb(devpriv->BADR3 + 2);
  98        if ((status & 0x80) == 0)
  99                return 0;
 100        return -EBUSY;
 101}
 102
 103static int cb_pcimdas_ai_rinsn(struct comedi_device *dev,
 104                               struct comedi_subdevice *s,
 105                               struct comedi_insn *insn, unsigned int *data)
 106{
 107        struct cb_pcimdas_private *devpriv = dev->private;
 108        int n;
 109        unsigned int d;
 110        int chan = CR_CHAN(insn->chanspec);
 111        unsigned short chanlims;
 112        int maxchans;
 113        int ret;
 114
 115        /*  only support sw initiated reads from a single channel */
 116
 117        /* check channel number */
 118        if ((inb(devpriv->BADR3 + 2) & 0x20) == 0)      /* differential mode */
 119                maxchans = s->n_chan / 2;
 120        else
 121                maxchans = s->n_chan;
 122
 123        if (chan > (maxchans - 1))
 124                return -ETIMEDOUT;      /* *** Wrong error code. Fixme. */
 125
 126        /* configure for sw initiated read */
 127        d = inb(devpriv->BADR3 + 5);
 128        if ((d & 0x03) > 0) {   /* only reset if needed. */
 129                d = d & 0xfd;
 130                outb(d, devpriv->BADR3 + 5);
 131        }
 132
 133        /* set bursting off, conversions on */
 134        outb(0x01, devpriv->BADR3 + 6);
 135
 136        /* set range to 10V. UP/BP is controlled by a switch on the board */
 137        outb(0x00, devpriv->BADR3 + 7);
 138
 139        /*
 140         * write channel limits to multiplexer, set Low (bits 0-3) and
 141         * High (bits 4-7) channels to chan.
 142         */
 143        chanlims = chan | (chan << 4);
 144        outb(chanlims, devpriv->BADR3 + 0);
 145
 146        /* convert n samples */
 147        for (n = 0; n < insn->n; n++) {
 148                /* trigger conversion */
 149                outw(0, devpriv->daqio + 0);
 150
 151                /* wait for conversion to end */
 152                ret = comedi_timeout(dev, s, insn, cb_pcimdas_ai_eoc, 0);
 153                if (ret)
 154                        return ret;
 155
 156                /* read data */
 157                data[n] = inw(devpriv->daqio + 0);
 158        }
 159
 160        /* return the number of samples read/written */
 161        return n;
 162}
 163
 164static int cb_pcimdas_ao_insn_write(struct comedi_device *dev,
 165                                    struct comedi_subdevice *s,
 166                                    struct comedi_insn *insn,
 167                                    unsigned int *data)
 168{
 169        struct cb_pcimdas_private *devpriv = dev->private;
 170        unsigned int chan = CR_CHAN(insn->chanspec);
 171        unsigned int val = s->readback[chan];
 172        unsigned int reg = (chan) ? DAC1_OFFSET : DAC0_OFFSET;
 173        int i;
 174
 175        for (i = 0; i < insn->n; i++) {
 176                val = data[i];
 177                outw(val, devpriv->daqio + reg);
 178        }
 179        s->readback[chan] = val;
 180
 181        return insn->n;
 182}
 183
 184static int cb_pcimdas_auto_attach(struct comedi_device *dev,
 185                                            unsigned long context_unused)
 186{
 187        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 188        struct cb_pcimdas_private *devpriv;
 189        struct comedi_subdevice *s;
 190        int ret;
 191
 192        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 193        if (!devpriv)
 194                return -ENOMEM;
 195
 196        ret = comedi_pci_enable(dev);
 197        if (ret)
 198                return ret;
 199
 200        devpriv->daqio = pci_resource_start(pcidev, 2);
 201        devpriv->BADR3 = pci_resource_start(pcidev, 3);
 202        dev->iobase = pci_resource_start(pcidev, 4);
 203
 204        ret = comedi_alloc_subdevices(dev, 3);
 205        if (ret)
 206                return ret;
 207
 208        s = &dev->subdevices[0];
 209        /* dev->read_subdev=s; */
 210        /*  analog input subdevice */
 211        s->type = COMEDI_SUBD_AI;
 212        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 213        s->n_chan = 16;
 214        s->maxdata = 0xffff;
 215        s->range_table = &range_unknown;
 216        s->len_chanlist = 1;    /*  This is the maximum chanlist length that */
 217        /*  the board can handle */
 218        s->insn_read = cb_pcimdas_ai_rinsn;
 219
 220        s = &dev->subdevices[1];
 221        /*  analog output subdevice */
 222        s->type = COMEDI_SUBD_AO;
 223        s->subdev_flags = SDF_WRITABLE;
 224        s->n_chan = 2;
 225        s->maxdata = 0xfff;
 226        /* ranges are hardware settable, but not software readable. */
 227        s->range_table = &range_unknown;
 228        s->insn_write = cb_pcimdas_ao_insn_write;
 229
 230        ret = comedi_alloc_subdev_readback(s);
 231        if (ret)
 232                return ret;
 233
 234        s = &dev->subdevices[2];
 235        /* digital i/o subdevice */
 236        ret = subdev_8255_init(dev, s, NULL, 0x00);
 237        if (ret)
 238                return ret;
 239
 240        return 0;
 241}
 242
 243static struct comedi_driver cb_pcimdas_driver = {
 244        .driver_name    = "cb_pcimdas",
 245        .module         = THIS_MODULE,
 246        .auto_attach    = cb_pcimdas_auto_attach,
 247        .detach         = comedi_pci_detach,
 248};
 249
 250static int cb_pcimdas_pci_probe(struct pci_dev *dev,
 251                                const struct pci_device_id *id)
 252{
 253        return comedi_pci_auto_config(dev, &cb_pcimdas_driver,
 254                                      id->driver_data);
 255}
 256
 257static const struct pci_device_id cb_pcimdas_pci_table[] = {
 258        { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0056) },       /* PCIM-DAS1602/16 */
 259        { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0115) },       /* PCIe-DAS1602/16 */
 260        { 0 }
 261};
 262MODULE_DEVICE_TABLE(pci, cb_pcimdas_pci_table);
 263
 264static struct pci_driver cb_pcimdas_pci_driver = {
 265        .name           = "cb_pcimdas",
 266        .id_table       = cb_pcimdas_pci_table,
 267        .probe          = cb_pcimdas_pci_probe,
 268        .remove         = comedi_pci_auto_unconfig,
 269};
 270module_comedi_pci_driver(cb_pcimdas_driver, cb_pcimdas_pci_driver);
 271
 272MODULE_AUTHOR("Comedi http://www.comedi.org");
 273MODULE_DESCRIPTION("Comedi driver for PCIM-DAS1602/16 and PCIe-DAS1602/16");
 274MODULE_LICENSE("GPL");
 275
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.