linux/drivers/comedi/drivers/addi_apci_3501.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * addi_apci_3501.c
   4 * Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
   5 * Project manager: Eric Stolz
   6 *
   7 *      ADDI-DATA GmbH
   8 *      Dieselstrasse 3
   9 *      D-77833 Ottersweier
  10 *      Tel: +19(0)7223/9493-0
  11 *      Fax: +49(0)7223/9493-92
  12 *      http://www.addi-data.com
  13 *      info@addi-data.com
  14 */
  15
  16/*
  17 * Driver: addi_apci_3501
  18 * Description: ADDI-DATA APCI-3501 Analog output board
  19 * Devices: [ADDI-DATA] APCI-3501 (addi_apci_3501)
  20 * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
  21 * Updated: Mon, 20 Jun 2016 10:57:01 -0700
  22 * Status: untested
  23 *
  24 * Configuration Options: not applicable, uses comedi PCI auto config
  25 *
  26 * This board has the following features:
  27 *   - 4 or 8 analog output channels
  28 *   - 2 optically isolated digital inputs
  29 *   - 2 optically isolated digital outputs
  30 *   - 1 12-bit watchdog/timer
  31 *
  32 * There are 2 versions of the APCI-3501:
  33 *   - APCI-3501-4  4 analog output channels
  34 *   - APCI-3501-8  8 analog output channels
  35 *
  36 * These boards use the same PCI Vendor/Device IDs. The number of output
  37 * channels used by this driver is determined by reading the EEPROM on
  38 * the board.
  39 *
  40 * The watchdog/timer subdevice is not currently supported.
  41 */
  42
  43#include <linux/module.h>
  44
  45#include "../comedi_pci.h"
  46#include "amcc_s5933.h"
  47
  48/*
  49 * PCI bar 1 register I/O map
  50 */
  51#define APCI3501_AO_CTRL_STATUS_REG             0x00
  52#define APCI3501_AO_CTRL_BIPOLAR                BIT(0)
  53#define APCI3501_AO_STATUS_READY                BIT(8)
  54#define APCI3501_AO_DATA_REG                    0x04
  55#define APCI3501_AO_DATA_CHAN(x)                ((x) << 0)
  56#define APCI3501_AO_DATA_VAL(x)                 ((x) << 8)
  57#define APCI3501_AO_DATA_BIPOLAR                BIT(31)
  58#define APCI3501_AO_TRIG_SCS_REG                0x08
  59#define APCI3501_TIMER_BASE                     0x20
  60#define APCI3501_DO_REG                         0x40
  61#define APCI3501_DI_REG                         0x50
  62
  63/*
  64 * AMCC S5933 NVRAM
  65 */
  66#define NVRAM_USER_DATA_START   0x100
  67
  68#define NVCMD_BEGIN_READ        (0x7 << 5)
  69#define NVCMD_LOAD_LOW          (0x4 << 5)
  70#define NVCMD_LOAD_HIGH         (0x5 << 5)
  71
  72/*
  73 * Function types stored in the eeprom
  74 */
  75#define EEPROM_DIGITALINPUT             0
  76#define EEPROM_DIGITALOUTPUT            1
  77#define EEPROM_ANALOGINPUT              2
  78#define EEPROM_ANALOGOUTPUT             3
  79#define EEPROM_TIMER                    4
  80#define EEPROM_WATCHDOG                 5
  81#define EEPROM_TIMER_WATCHDOG_COUNTER   10
  82
  83struct apci3501_private {
  84        unsigned long amcc;
  85        unsigned char timer_mode;
  86};
  87
  88static const struct comedi_lrange apci3501_ao_range = {
  89        2, {
  90                BIP_RANGE(10),
  91                UNI_RANGE(10)
  92        }
  93};
  94
  95static int apci3501_wait_for_dac(struct comedi_device *dev)
  96{
  97        unsigned int status;
  98
  99        do {
 100                status = inl(dev->iobase + APCI3501_AO_CTRL_STATUS_REG);
 101        } while (!(status & APCI3501_AO_STATUS_READY));
 102
 103        return 0;
 104}
 105
 106static int apci3501_ao_insn_write(struct comedi_device *dev,
 107                                  struct comedi_subdevice *s,
 108                                  struct comedi_insn *insn,
 109                                  unsigned int *data)
 110{
 111        unsigned int chan = CR_CHAN(insn->chanspec);
 112        unsigned int range = CR_RANGE(insn->chanspec);
 113        unsigned int cfg = APCI3501_AO_DATA_CHAN(chan);
 114        int ret;
 115        int i;
 116
 117        /*
 118         * All analog output channels have the same output range.
 119         *      14-bit bipolar: 0-10V
 120         *      13-bit unipolar: +/-10V
 121         * Changing the range of one channel changes all of them!
 122         */
 123        if (range) {
 124                outl(0, dev->iobase + APCI3501_AO_CTRL_STATUS_REG);
 125        } else {
 126                cfg |= APCI3501_AO_DATA_BIPOLAR;
 127                outl(APCI3501_AO_CTRL_BIPOLAR,
 128                     dev->iobase + APCI3501_AO_CTRL_STATUS_REG);
 129        }
 130
 131        for (i = 0; i < insn->n; i++) {
 132                unsigned int val = data[i];
 133
 134                if (range == 1) {
 135                        if (data[i] > 0x1fff) {
 136                                dev_err(dev->class_dev,
 137                                        "Unipolar resolution is only 13-bits\n");
 138                                return -EINVAL;
 139                        }
 140                }
 141
 142                ret = apci3501_wait_for_dac(dev);
 143                if (ret)
 144                        return ret;
 145
 146                outl(cfg | APCI3501_AO_DATA_VAL(val),
 147                     dev->iobase + APCI3501_AO_DATA_REG);
 148
 149                s->readback[chan] = val;
 150        }
 151
 152        return insn->n;
 153}
 154
 155static int apci3501_di_insn_bits(struct comedi_device *dev,
 156                                 struct comedi_subdevice *s,
 157                                 struct comedi_insn *insn,
 158                                 unsigned int *data)
 159{
 160        data[1] = inl(dev->iobase + APCI3501_DI_REG) & 0x3;
 161
 162        return insn->n;
 163}
 164
 165static int apci3501_do_insn_bits(struct comedi_device *dev,
 166                                 struct comedi_subdevice *s,
 167                                 struct comedi_insn *insn,
 168                                 unsigned int *data)
 169{
 170        s->state = inl(dev->iobase + APCI3501_DO_REG);
 171
 172        if (comedi_dio_update_state(s, data))
 173                outl(s->state, dev->iobase + APCI3501_DO_REG);
 174
 175        data[1] = s->state;
 176
 177        return insn->n;
 178}
 179
 180static void apci3501_eeprom_wait(unsigned long iobase)
 181{
 182        unsigned char val;
 183
 184        do {
 185                val = inb(iobase + AMCC_OP_REG_MCSR_NVCMD);
 186        } while (val & 0x80);
 187}
 188
 189static unsigned short apci3501_eeprom_readw(unsigned long iobase,
 190                                            unsigned short addr)
 191{
 192        unsigned short val = 0;
 193        unsigned char tmp;
 194        unsigned char i;
 195
 196        /* Add the offset to the start of the user data */
 197        addr += NVRAM_USER_DATA_START;
 198
 199        for (i = 0; i < 2; i++) {
 200                /* Load the low 8 bit address */
 201                outb(NVCMD_LOAD_LOW, iobase + AMCC_OP_REG_MCSR_NVCMD);
 202                apci3501_eeprom_wait(iobase);
 203                outb((addr + i) & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA);
 204                apci3501_eeprom_wait(iobase);
 205
 206                /* Load the high 8 bit address */
 207                outb(NVCMD_LOAD_HIGH, iobase + AMCC_OP_REG_MCSR_NVCMD);
 208                apci3501_eeprom_wait(iobase);
 209                outb(((addr + i) >> 8) & 0xff,
 210                     iobase + AMCC_OP_REG_MCSR_NVDATA);
 211                apci3501_eeprom_wait(iobase);
 212
 213                /* Read the eeprom data byte */
 214                outb(NVCMD_BEGIN_READ, iobase + AMCC_OP_REG_MCSR_NVCMD);
 215                apci3501_eeprom_wait(iobase);
 216                tmp = inb(iobase + AMCC_OP_REG_MCSR_NVDATA);
 217                apci3501_eeprom_wait(iobase);
 218
 219                if (i == 0)
 220                        val |= tmp;
 221                else
 222                        val |= (tmp << 8);
 223        }
 224
 225        return val;
 226}
 227
 228static int apci3501_eeprom_get_ao_n_chan(struct comedi_device *dev)
 229{
 230        struct apci3501_private *devpriv = dev->private;
 231        unsigned char nfuncs;
 232        int i;
 233
 234        nfuncs = apci3501_eeprom_readw(devpriv->amcc, 10) & 0xff;
 235
 236        /* Read functionality details */
 237        for (i = 0; i < nfuncs; i++) {
 238                unsigned short offset = i * 4;
 239                unsigned short addr;
 240                unsigned char func;
 241                unsigned short val;
 242
 243                func = apci3501_eeprom_readw(devpriv->amcc, 12 + offset) & 0x3f;
 244                addr = apci3501_eeprom_readw(devpriv->amcc, 14 + offset);
 245
 246                if (func == EEPROM_ANALOGOUTPUT) {
 247                        val = apci3501_eeprom_readw(devpriv->amcc, addr + 10);
 248                        return (val >> 4) & 0x3ff;
 249                }
 250        }
 251        return 0;
 252}
 253
 254static int apci3501_eeprom_insn_read(struct comedi_device *dev,
 255                                     struct comedi_subdevice *s,
 256                                     struct comedi_insn *insn,
 257                                     unsigned int *data)
 258{
 259        struct apci3501_private *devpriv = dev->private;
 260        unsigned short addr = CR_CHAN(insn->chanspec);
 261        unsigned int val;
 262        unsigned int i;
 263
 264        if (insn->n) {
 265                /* No point reading the same EEPROM location more than once. */
 266                val = apci3501_eeprom_readw(devpriv->amcc, 2 * addr);
 267                for (i = 0; i < insn->n; i++)
 268                        data[i] = val;
 269        }
 270
 271        return insn->n;
 272}
 273
 274static int apci3501_reset(struct comedi_device *dev)
 275{
 276        unsigned int val;
 277        int chan;
 278        int ret;
 279
 280        /* Reset all digital outputs to "0" */
 281        outl(0x0, dev->iobase + APCI3501_DO_REG);
 282
 283        /* Default all analog outputs to 0V (bipolar) */
 284        outl(APCI3501_AO_CTRL_BIPOLAR,
 285             dev->iobase + APCI3501_AO_CTRL_STATUS_REG);
 286        val = APCI3501_AO_DATA_BIPOLAR | APCI3501_AO_DATA_VAL(0);
 287
 288        /* Set all analog output channels */
 289        for (chan = 0; chan < 8; chan++) {
 290                ret = apci3501_wait_for_dac(dev);
 291                if (ret) {
 292                        dev_warn(dev->class_dev,
 293                                 "%s: DAC not-ready for channel %i\n",
 294                                 __func__, chan);
 295                } else {
 296                        outl(val | APCI3501_AO_DATA_CHAN(chan),
 297                             dev->iobase + APCI3501_AO_DATA_REG);
 298                }
 299        }
 300
 301        return 0;
 302}
 303
 304static int apci3501_auto_attach(struct comedi_device *dev,
 305                                unsigned long context_unused)
 306{
 307        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 308        struct apci3501_private *devpriv;
 309        struct comedi_subdevice *s;
 310        int ao_n_chan;
 311        int ret;
 312
 313        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 314        if (!devpriv)
 315                return -ENOMEM;
 316
 317        ret = comedi_pci_enable(dev);
 318        if (ret)
 319                return ret;
 320
 321        devpriv->amcc = pci_resource_start(pcidev, 0);
 322        dev->iobase = pci_resource_start(pcidev, 1);
 323
 324        ao_n_chan = apci3501_eeprom_get_ao_n_chan(dev);
 325
 326        ret = comedi_alloc_subdevices(dev, 5);
 327        if (ret)
 328                return ret;
 329
 330        /* Initialize the analog output subdevice */
 331        s = &dev->subdevices[0];
 332        if (ao_n_chan) {
 333                s->type         = COMEDI_SUBD_AO;
 334                s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
 335                s->n_chan       = ao_n_chan;
 336                s->maxdata      = 0x3fff;
 337                s->range_table  = &apci3501_ao_range;
 338                s->insn_write   = apci3501_ao_insn_write;
 339
 340                ret = comedi_alloc_subdev_readback(s);
 341                if (ret)
 342                        return ret;
 343        } else {
 344                s->type         = COMEDI_SUBD_UNUSED;
 345        }
 346
 347        /* Initialize the digital input subdevice */
 348        s = &dev->subdevices[1];
 349        s->type         = COMEDI_SUBD_DI;
 350        s->subdev_flags = SDF_READABLE;
 351        s->n_chan       = 2;
 352        s->maxdata      = 1;
 353        s->range_table  = &range_digital;
 354        s->insn_bits    = apci3501_di_insn_bits;
 355
 356        /* Initialize the digital output subdevice */
 357        s = &dev->subdevices[2];
 358        s->type         = COMEDI_SUBD_DO;
 359        s->subdev_flags = SDF_WRITABLE;
 360        s->n_chan       = 2;
 361        s->maxdata      = 1;
 362        s->range_table  = &range_digital;
 363        s->insn_bits    = apci3501_do_insn_bits;
 364
 365        /* Timer/Watchdog subdevice */
 366        s = &dev->subdevices[3];
 367        s->type         = COMEDI_SUBD_UNUSED;
 368
 369        /* Initialize the eeprom subdevice */
 370        s = &dev->subdevices[4];
 371        s->type         = COMEDI_SUBD_MEMORY;
 372        s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
 373        s->n_chan       = 256;
 374        s->maxdata      = 0xffff;
 375        s->insn_read    = apci3501_eeprom_insn_read;
 376
 377        apci3501_reset(dev);
 378        return 0;
 379}
 380
 381static void apci3501_detach(struct comedi_device *dev)
 382{
 383        if (dev->iobase)
 384                apci3501_reset(dev);
 385        comedi_pci_detach(dev);
 386}
 387
 388static struct comedi_driver apci3501_driver = {
 389        .driver_name    = "addi_apci_3501",
 390        .module         = THIS_MODULE,
 391        .auto_attach    = apci3501_auto_attach,
 392        .detach         = apci3501_detach,
 393};
 394
 395static int apci3501_pci_probe(struct pci_dev *dev,
 396                              const struct pci_device_id *id)
 397{
 398        return comedi_pci_auto_config(dev, &apci3501_driver, id->driver_data);
 399}
 400
 401static const struct pci_device_id apci3501_pci_table[] = {
 402        { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x3001) },
 403        { 0 }
 404};
 405MODULE_DEVICE_TABLE(pci, apci3501_pci_table);
 406
 407static struct pci_driver apci3501_pci_driver = {
 408        .name           = "addi_apci_3501",
 409        .id_table       = apci3501_pci_table,
 410        .probe          = apci3501_pci_probe,
 411        .remove         = comedi_pci_auto_unconfig,
 412};
 413module_comedi_pci_driver(apci3501_driver, apci3501_pci_driver);
 414
 415MODULE_DESCRIPTION("ADDI-DATA APCI-3501 Analog output board");
 416MODULE_AUTHOR("Comedi https://www.comedi.org");
 417MODULE_LICENSE("GPL");
 418