linux/drivers/comedi/drivers/addi_apci_1032.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * addi_apci_1032.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_1032
  18 * Description: ADDI-DATA APCI-1032 Digital Input Board
  19 * Author: ADDI-DATA GmbH <info@addi-data.com>,
  20 *   H Hartley Sweeten <hsweeten@visionengravers.com>
  21 * Status: untested
  22 * Devices: [ADDI-DATA] APCI-1032 (addi_apci_1032)
  23 *
  24 * Configuration options:
  25 *   None; devices are configured automatically.
  26 *
  27 * This driver models the APCI-1032 as a 32-channel, digital input subdevice
  28 * plus an additional digital input subdevice to handle change-of-state (COS)
  29 * interrupts (if an interrupt handler can be set up successfully).
  30 *
  31 * The COS subdevice supports comedi asynchronous read commands.
  32 *
  33 * Change-Of-State (COS) interrupt configuration:
  34 *
  35 * Channels 0 to 15 are interruptible. These channels can be configured
  36 * to generate interrupts based on AND/OR logic for the desired channels.
  37 *
  38 *   OR logic:
  39 *   - reacts to rising or falling edges
  40 *   - interrupt is generated when any enabled channel meets the desired
  41 *     interrupt condition
  42 *
  43 *   AND logic:
  44 *   - reacts to changes in level of the selected inputs
  45 *   - interrupt is generated when all enabled channels meet the desired
  46 *     interrupt condition
  47 *   - after an interrupt, a change in level must occur on the selected
  48 *     inputs to release the IRQ logic
  49 *
  50 * The COS subdevice must be configured before setting up a comedi
  51 * asynchronous command:
  52 *
  53 *   data[0] : INSN_CONFIG_DIGITAL_TRIG
  54 *   data[1] : trigger number (= 0)
  55 *   data[2] : configuration operation:
  56 *             - COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
  57 *             - COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
  58 *             - COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
  59 *   data[3] : left-shift for data[4] and data[5]
  60 *   data[4] : rising-edge/high level channels
  61 *   data[5] : falling-edge/low level channels
  62 */
  63
  64#include <linux/module.h>
  65#include <linux/interrupt.h>
  66
  67#include "../comedi_pci.h"
  68#include "amcc_s5933.h"
  69
  70/*
  71 * I/O Register Map
  72 */
  73#define APCI1032_DI_REG                 0x00
  74#define APCI1032_MODE1_REG              0x04
  75#define APCI1032_MODE2_REG              0x08
  76#define APCI1032_STATUS_REG             0x0c
  77#define APCI1032_CTRL_REG               0x10
  78#define APCI1032_CTRL_INT_MODE(x)       (((x) & 0x1) << 1)
  79#define APCI1032_CTRL_INT_OR            APCI1032_CTRL_INT_MODE(0)
  80#define APCI1032_CTRL_INT_AND           APCI1032_CTRL_INT_MODE(1)
  81#define APCI1032_CTRL_INT_ENA           BIT(2)
  82
  83struct apci1032_private {
  84        unsigned long amcc_iobase;      /* base of AMCC I/O registers */
  85        unsigned int mode1;     /* rising-edge/high level channels */
  86        unsigned int mode2;     /* falling-edge/low level channels */
  87        unsigned int ctrl;      /* interrupt mode OR (edge) . AND (level) */
  88};
  89
  90static int apci1032_reset(struct comedi_device *dev)
  91{
  92        /* disable the interrupts */
  93        outl(0x0, dev->iobase + APCI1032_CTRL_REG);
  94        /* Reset the interrupt status register */
  95        inl(dev->iobase + APCI1032_STATUS_REG);
  96        /* Disable the and/or interrupt */
  97        outl(0x0, dev->iobase + APCI1032_MODE1_REG);
  98        outl(0x0, dev->iobase + APCI1032_MODE2_REG);
  99
 100        return 0;
 101}
 102
 103static int apci1032_cos_insn_config(struct comedi_device *dev,
 104                                    struct comedi_subdevice *s,
 105                                    struct comedi_insn *insn,
 106                                    unsigned int *data)
 107{
 108        struct apci1032_private *devpriv = dev->private;
 109        unsigned int shift, oldmask, himask, lomask;
 110
 111        switch (data[0]) {
 112        case INSN_CONFIG_DIGITAL_TRIG:
 113                if (data[1] != 0)
 114                        return -EINVAL;
 115                shift = data[3];
 116                if (shift < 32) {
 117                        oldmask = (1U << shift) - 1;
 118                        himask = data[4] << shift;
 119                        lomask = data[5] << shift;
 120                } else {
 121                        oldmask = 0xffffffffu;
 122                        himask = 0;
 123                        lomask = 0;
 124                }
 125                switch (data[2]) {
 126                case COMEDI_DIGITAL_TRIG_DISABLE:
 127                        devpriv->ctrl = 0;
 128                        devpriv->mode1 = 0;
 129                        devpriv->mode2 = 0;
 130                        apci1032_reset(dev);
 131                        break;
 132                case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
 133                        if (devpriv->ctrl != (APCI1032_CTRL_INT_ENA |
 134                                              APCI1032_CTRL_INT_OR)) {
 135                                /* switching to 'OR' mode */
 136                                devpriv->ctrl = APCI1032_CTRL_INT_ENA |
 137                                                APCI1032_CTRL_INT_OR;
 138                                /* wipe old channels */
 139                                devpriv->mode1 = 0;
 140                                devpriv->mode2 = 0;
 141                        } else {
 142                                /* preserve unspecified channels */
 143                                devpriv->mode1 &= oldmask;
 144                                devpriv->mode2 &= oldmask;
 145                        }
 146                        /* configure specified channels */
 147                        devpriv->mode1 |= himask;
 148                        devpriv->mode2 |= lomask;
 149                        break;
 150                case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
 151                        if (devpriv->ctrl != (APCI1032_CTRL_INT_ENA |
 152                                              APCI1032_CTRL_INT_AND)) {
 153                                /* switching to 'AND' mode */
 154                                devpriv->ctrl = APCI1032_CTRL_INT_ENA |
 155                                                APCI1032_CTRL_INT_AND;
 156                                /* wipe old channels */
 157                                devpriv->mode1 = 0;
 158                                devpriv->mode2 = 0;
 159                        } else {
 160                                /* preserve unspecified channels */
 161                                devpriv->mode1 &= oldmask;
 162                                devpriv->mode2 &= oldmask;
 163                        }
 164                        /* configure specified channels */
 165                        devpriv->mode1 |= himask;
 166                        devpriv->mode2 |= lomask;
 167                        break;
 168                default:
 169                        return -EINVAL;
 170                }
 171                break;
 172        default:
 173                return -EINVAL;
 174        }
 175
 176        return insn->n;
 177}
 178
 179static int apci1032_cos_insn_bits(struct comedi_device *dev,
 180                                  struct comedi_subdevice *s,
 181                                  struct comedi_insn *insn,
 182                                  unsigned int *data)
 183{
 184        data[1] = s->state;
 185
 186        return 0;
 187}
 188
 189static int apci1032_cos_cmdtest(struct comedi_device *dev,
 190                                struct comedi_subdevice *s,
 191                                struct comedi_cmd *cmd)
 192{
 193        int err = 0;
 194
 195        /* Step 1 : check if triggers are trivially valid */
 196
 197        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 198        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
 199        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
 200        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 201        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
 202
 203        if (err)
 204                return 1;
 205
 206        /* Step 2a : make sure trigger sources are unique */
 207        /* Step 2b : and mutually compatible */
 208
 209        /* Step 3: check if arguments are trivially valid */
 210
 211        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 212        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 213        err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 214        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 215                                           cmd->chanlist_len);
 216        err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 217
 218        if (err)
 219                return 3;
 220
 221        /* Step 4: fix up any arguments */
 222
 223        /* Step 5: check channel list if it exists */
 224
 225        return 0;
 226}
 227
 228/*
 229 * Change-Of-State (COS) 'do_cmd' operation
 230 *
 231 * Enable the COS interrupt as configured by apci1032_cos_insn_config().
 232 */
 233static int apci1032_cos_cmd(struct comedi_device *dev,
 234                            struct comedi_subdevice *s)
 235{
 236        struct apci1032_private *devpriv = dev->private;
 237
 238        if (!devpriv->ctrl) {
 239                dev_warn(dev->class_dev,
 240                         "Interrupts disabled due to mode configuration!\n");
 241                return -EINVAL;
 242        }
 243
 244        outl(devpriv->mode1, dev->iobase + APCI1032_MODE1_REG);
 245        outl(devpriv->mode2, dev->iobase + APCI1032_MODE2_REG);
 246        outl(devpriv->ctrl, dev->iobase + APCI1032_CTRL_REG);
 247
 248        return 0;
 249}
 250
 251static int apci1032_cos_cancel(struct comedi_device *dev,
 252                               struct comedi_subdevice *s)
 253{
 254        return apci1032_reset(dev);
 255}
 256
 257static irqreturn_t apci1032_interrupt(int irq, void *d)
 258{
 259        struct comedi_device *dev = d;
 260        struct apci1032_private *devpriv = dev->private;
 261        struct comedi_subdevice *s = dev->read_subdev;
 262        unsigned int ctrl;
 263        unsigned short val;
 264
 265        /* check interrupt is from this device */
 266        if ((inl(devpriv->amcc_iobase + AMCC_OP_REG_INTCSR) &
 267             INTCSR_INTR_ASSERTED) == 0)
 268                return IRQ_NONE;
 269
 270        /* check interrupt is enabled */
 271        ctrl = inl(dev->iobase + APCI1032_CTRL_REG);
 272        if ((ctrl & APCI1032_CTRL_INT_ENA) == 0)
 273                return IRQ_HANDLED;
 274
 275        /* disable the interrupt */
 276        outl(ctrl & ~APCI1032_CTRL_INT_ENA, dev->iobase + APCI1032_CTRL_REG);
 277
 278        s->state = inl(dev->iobase + APCI1032_STATUS_REG) & 0xffff;
 279        val = s->state;
 280        comedi_buf_write_samples(s, &val, 1);
 281        comedi_handle_events(dev, s);
 282
 283        /* enable the interrupt */
 284        outl(ctrl, dev->iobase + APCI1032_CTRL_REG);
 285
 286        return IRQ_HANDLED;
 287}
 288
 289static int apci1032_di_insn_bits(struct comedi_device *dev,
 290                                 struct comedi_subdevice *s,
 291                                 struct comedi_insn *insn,
 292                                 unsigned int *data)
 293{
 294        data[1] = inl(dev->iobase + APCI1032_DI_REG);
 295
 296        return insn->n;
 297}
 298
 299static int apci1032_auto_attach(struct comedi_device *dev,
 300                                unsigned long context_unused)
 301{
 302        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 303        struct apci1032_private *devpriv;
 304        struct comedi_subdevice *s;
 305        int ret;
 306
 307        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 308        if (!devpriv)
 309                return -ENOMEM;
 310
 311        ret = comedi_pci_enable(dev);
 312        if (ret)
 313                return ret;
 314
 315        devpriv->amcc_iobase = pci_resource_start(pcidev, 0);
 316        dev->iobase = pci_resource_start(pcidev, 1);
 317        apci1032_reset(dev);
 318        if (pcidev->irq > 0) {
 319                ret = request_irq(pcidev->irq, apci1032_interrupt, IRQF_SHARED,
 320                                  dev->board_name, dev);
 321                if (ret == 0)
 322                        dev->irq = pcidev->irq;
 323        }
 324
 325        ret = comedi_alloc_subdevices(dev, 2);
 326        if (ret)
 327                return ret;
 328
 329        /*  Allocate and Initialise DI Subdevice Structures */
 330        s = &dev->subdevices[0];
 331        s->type         = COMEDI_SUBD_DI;
 332        s->subdev_flags = SDF_READABLE;
 333        s->n_chan       = 32;
 334        s->maxdata      = 1;
 335        s->range_table  = &range_digital;
 336        s->insn_bits    = apci1032_di_insn_bits;
 337
 338        /* Change-Of-State (COS) interrupt subdevice */
 339        s = &dev->subdevices[1];
 340        if (dev->irq) {
 341                dev->read_subdev = s;
 342                s->type         = COMEDI_SUBD_DI;
 343                s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
 344                s->n_chan       = 1;
 345                s->maxdata      = 1;
 346                s->range_table  = &range_digital;
 347                s->insn_config  = apci1032_cos_insn_config;
 348                s->insn_bits    = apci1032_cos_insn_bits;
 349                s->len_chanlist = 1;
 350                s->do_cmdtest   = apci1032_cos_cmdtest;
 351                s->do_cmd       = apci1032_cos_cmd;
 352                s->cancel       = apci1032_cos_cancel;
 353        } else {
 354                s->type         = COMEDI_SUBD_UNUSED;
 355        }
 356
 357        return 0;
 358}
 359
 360static void apci1032_detach(struct comedi_device *dev)
 361{
 362        if (dev->iobase)
 363                apci1032_reset(dev);
 364        comedi_pci_detach(dev);
 365}
 366
 367static struct comedi_driver apci1032_driver = {
 368        .driver_name    = "addi_apci_1032",
 369        .module         = THIS_MODULE,
 370        .auto_attach    = apci1032_auto_attach,
 371        .detach         = apci1032_detach,
 372};
 373
 374static int apci1032_pci_probe(struct pci_dev *dev,
 375                              const struct pci_device_id *id)
 376{
 377        return comedi_pci_auto_config(dev, &apci1032_driver, id->driver_data);
 378}
 379
 380static const struct pci_device_id apci1032_pci_table[] = {
 381        { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1003) },
 382        { 0 }
 383};
 384MODULE_DEVICE_TABLE(pci, apci1032_pci_table);
 385
 386static struct pci_driver apci1032_pci_driver = {
 387        .name           = "addi_apci_1032",
 388        .id_table       = apci1032_pci_table,
 389        .probe          = apci1032_pci_probe,
 390        .remove         = comedi_pci_auto_unconfig,
 391};
 392module_comedi_pci_driver(apci1032_driver, apci1032_pci_driver);
 393
 394MODULE_AUTHOR("Comedi https://www.comedi.org");
 395MODULE_DESCRIPTION("ADDI-DATA APCI-1032, 32 channel DI boards");
 396MODULE_LICENSE("GPL");
 397