linux/drivers/comedi/drivers/adl_pci6208.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * adl_pci6208.c
   4 * Comedi driver for ADLink 6208 series cards
   5 *
   6 * COMEDI - Linux Control and Measurement Device Interface
   7 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   8 */
   9
  10/*
  11 * Driver: adl_pci6208
  12 * Description: ADLink PCI-6208/6216 Series Multi-channel Analog Output Cards
  13 * Devices: [ADLink] PCI-6208 (adl_pci6208), PCI-6216
  14 * Author: nsyeow <nsyeow@pd.jaring.my>
  15 * Updated: Wed, 11 Feb 2015 11:37:18 +0000
  16 * Status: untested
  17 *
  18 * Configuration Options: not applicable, uses PCI auto config
  19 *
  20 * All supported devices share the same PCI device ID and are treated as a
  21 * PCI-6216 with 16 analog output channels.  On a PCI-6208, the upper 8
  22 * channels exist in registers, but don't go to DAC chips.
  23 */
  24
  25#include <linux/module.h>
  26#include <linux/delay.h>
  27#include <linux/comedi/comedi_pci.h>
  28
  29/*
  30 * PCI-6208/6216-GL register map
  31 */
  32#define PCI6208_AO_CONTROL(x)           (0x00 + (2 * (x)))
  33#define PCI6208_AO_STATUS               0x00
  34#define PCI6208_AO_STATUS_DATA_SEND     BIT(0)
  35#define PCI6208_DIO                     0x40
  36#define PCI6208_DIO_DO_MASK             (0x0f)
  37#define PCI6208_DIO_DO_SHIFT            (0)
  38#define PCI6208_DIO_DI_MASK             (0xf0)
  39#define PCI6208_DIO_DI_SHIFT            (4)
  40
  41static int pci6208_ao_eoc(struct comedi_device *dev,
  42                          struct comedi_subdevice *s,
  43                          struct comedi_insn *insn,
  44                          unsigned long context)
  45{
  46        unsigned int status;
  47
  48        status = inw(dev->iobase + PCI6208_AO_STATUS);
  49        if ((status & PCI6208_AO_STATUS_DATA_SEND) == 0)
  50                return 0;
  51        return -EBUSY;
  52}
  53
  54static int pci6208_ao_insn_write(struct comedi_device *dev,
  55                                 struct comedi_subdevice *s,
  56                                 struct comedi_insn *insn,
  57                                 unsigned int *data)
  58{
  59        unsigned int chan = CR_CHAN(insn->chanspec);
  60        int ret;
  61        int i;
  62
  63        for (i = 0; i < insn->n; i++) {
  64                unsigned int val = data[i];
  65
  66                /* D/A transfer rate is 2.2us */
  67                ret = comedi_timeout(dev, s, insn, pci6208_ao_eoc, 0);
  68                if (ret)
  69                        return ret;
  70
  71                /* the hardware expects two's complement values */
  72                outw(comedi_offset_munge(s, val),
  73                     dev->iobase + PCI6208_AO_CONTROL(chan));
  74
  75                s->readback[chan] = val;
  76        }
  77
  78        return insn->n;
  79}
  80
  81static int pci6208_di_insn_bits(struct comedi_device *dev,
  82                                struct comedi_subdevice *s,
  83                                struct comedi_insn *insn,
  84                                unsigned int *data)
  85{
  86        unsigned int val;
  87
  88        val = inw(dev->iobase + PCI6208_DIO);
  89        val = (val & PCI6208_DIO_DI_MASK) >> PCI6208_DIO_DI_SHIFT;
  90
  91        data[1] = val;
  92
  93        return insn->n;
  94}
  95
  96static int pci6208_do_insn_bits(struct comedi_device *dev,
  97                                struct comedi_subdevice *s,
  98                                struct comedi_insn *insn,
  99                                unsigned int *data)
 100{
 101        if (comedi_dio_update_state(s, data))
 102                outw(s->state, dev->iobase + PCI6208_DIO);
 103
 104        data[1] = s->state;
 105
 106        return insn->n;
 107}
 108
 109static int pci6208_auto_attach(struct comedi_device *dev,
 110                               unsigned long context_unused)
 111{
 112        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 113        struct comedi_subdevice *s;
 114        unsigned int val;
 115        int ret;
 116
 117        ret = comedi_pci_enable(dev);
 118        if (ret)
 119                return ret;
 120        dev->iobase = pci_resource_start(pcidev, 2);
 121
 122        ret = comedi_alloc_subdevices(dev, 3);
 123        if (ret)
 124                return ret;
 125
 126        s = &dev->subdevices[0];
 127        /* analog output subdevice */
 128        s->type         = COMEDI_SUBD_AO;
 129        s->subdev_flags = SDF_WRITABLE;
 130        s->n_chan       = 16;   /* Only 8 usable on PCI-6208 */
 131        s->maxdata      = 0xffff;
 132        s->range_table  = &range_bipolar10;
 133        s->insn_write   = pci6208_ao_insn_write;
 134
 135        ret = comedi_alloc_subdev_readback(s);
 136        if (ret)
 137                return ret;
 138
 139        s = &dev->subdevices[1];
 140        /* digital input subdevice */
 141        s->type         = COMEDI_SUBD_DI;
 142        s->subdev_flags = SDF_READABLE;
 143        s->n_chan       = 4;
 144        s->maxdata      = 1;
 145        s->range_table  = &range_digital;
 146        s->insn_bits    = pci6208_di_insn_bits;
 147
 148        s = &dev->subdevices[2];
 149        /* digital output subdevice */
 150        s->type         = COMEDI_SUBD_DO;
 151        s->subdev_flags = SDF_WRITABLE;
 152        s->n_chan       = 4;
 153        s->maxdata      = 1;
 154        s->range_table  = &range_digital;
 155        s->insn_bits    = pci6208_do_insn_bits;
 156
 157        /*
 158         * Get the read back signals from the digital outputs
 159         * and save it as the initial state for the subdevice.
 160         */
 161        val = inw(dev->iobase + PCI6208_DIO);
 162        val = (val & PCI6208_DIO_DO_MASK) >> PCI6208_DIO_DO_SHIFT;
 163        s->state        = val;
 164
 165        return 0;
 166}
 167
 168static struct comedi_driver adl_pci6208_driver = {
 169        .driver_name    = "adl_pci6208",
 170        .module         = THIS_MODULE,
 171        .auto_attach    = pci6208_auto_attach,
 172        .detach         = comedi_pci_detach,
 173};
 174
 175static int adl_pci6208_pci_probe(struct pci_dev *dev,
 176                                 const struct pci_device_id *id)
 177{
 178        return comedi_pci_auto_config(dev, &adl_pci6208_driver,
 179                                      id->driver_data);
 180}
 181
 182static const struct pci_device_id adl_pci6208_pci_table[] = {
 183        { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x6208) },
 184        { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
 185                         0x9999, 0x6208) },
 186        { 0 }
 187};
 188MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table);
 189
 190static struct pci_driver adl_pci6208_pci_driver = {
 191        .name           = "adl_pci6208",
 192        .id_table       = adl_pci6208_pci_table,
 193        .probe          = adl_pci6208_pci_probe,
 194        .remove         = comedi_pci_auto_unconfig,
 195};
 196module_comedi_pci_driver(adl_pci6208_driver, adl_pci6208_pci_driver);
 197
 198MODULE_AUTHOR("Comedi https://www.comedi.org");
 199MODULE_DESCRIPTION("Comedi driver for ADLink 6208 series cards");
 200MODULE_LICENSE("GPL");
 201