linux/drivers/staging/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;
 110
 111        switch (data[0]) {
 112        case INSN_CONFIG_DIGITAL_TRIG:
 113                if (data[1] != 0)
 114                        return -EINVAL;
 115                shift = data[3];
 116                oldmask = (1U << shift) - 1;
 117                switch (data[2]) {
 118                case COMEDI_DIGITAL_TRIG_DISABLE:
 119                        devpriv->ctrl = 0;
 120                        devpriv->mode1 = 0;
 121                        devpriv->mode2 = 0;
 122                        apci1032_reset(dev);
 123                        break;
 124                case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
 125                        if (devpriv->ctrl != (APCI1032_CTRL_INT_ENA |
 126                                              APCI1032_CTRL_INT_OR)) {
 127                                /* switching to 'OR' mode */
 128                                devpriv->ctrl = APCI1032_CTRL_INT_ENA |
 129                                                APCI1032_CTRL_INT_OR;
 130                                /* wipe old channels */
 131                                devpriv->mode1 = 0;
 132                                devpriv->mode2 = 0;
 133                        } else {
 134                                /* preserve unspecified channels */
 135                                devpriv->mode1 &= oldmask;
 136                                devpriv->mode2 &= oldmask;
 137                        }
 138                        /* configure specified channels */
 139                        devpriv->mode1 |= data[4] << shift;
 140                        devpriv->mode2 |= data[5] << shift;
 141                        break;
 142                case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
 143                        if (devpriv->ctrl != (APCI1032_CTRL_INT_ENA |
 144                                              APCI1032_CTRL_INT_AND)) {
 145                                /* switching to 'AND' mode */
 146                                devpriv->ctrl = APCI1032_CTRL_INT_ENA |
 147                                                APCI1032_CTRL_INT_AND;
 148                                /* wipe old channels */
 149                                devpriv->mode1 = 0;
 150                                devpriv->mode2 = 0;
 151                        } else {
 152                                /* preserve unspecified channels */
 153                                devpriv->mode1 &= oldmask;
 154                                devpriv->mode2 &= oldmask;
 155                        }
 156                        /* configure specified channels */
 157                        devpriv->mode1 |= data[4] << shift;
 158                        devpriv->mode2 |= data[5] << shift;
 159                        break;
 160                default:
 161                        return -EINVAL;
 162                }
 163                break;
 164        default:
 165                return -EINVAL;
 166        }
 167
 168        return insn->n;
 169}
 170
 171static int apci1032_cos_insn_bits(struct comedi_device *dev,
 172                                  struct comedi_subdevice *s,
 173                                  struct comedi_insn *insn,
 174                                  unsigned int *data)
 175{
 176        data[1] = s->state;
 177
 178        return 0;
 179}
 180
 181static int apci1032_cos_cmdtest(struct comedi_device *dev,
 182                                struct comedi_subdevice *s,
 183                                struct comedi_cmd *cmd)
 184{
 185        int err = 0;
 186
 187        /* Step 1 : check if triggers are trivially valid */
 188
 189        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 190        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
 191        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
 192        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 193        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
 194
 195        if (err)
 196                return 1;
 197
 198        /* Step 2a : make sure trigger sources are unique */
 199        /* Step 2b : and mutually compatible */
 200
 201        /* Step 3: check if arguments are trivially valid */
 202
 203        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 204        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 205        err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 206        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 207                                           cmd->chanlist_len);
 208        err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 209
 210        if (err)
 211                return 3;
 212
 213        /* Step 4: fix up any arguments */
 214
 215        /* Step 5: check channel list if it exists */
 216
 217        return 0;
 218}
 219
 220/*
 221 * Change-Of-State (COS) 'do_cmd' operation
 222 *
 223 * Enable the COS interrupt as configured by apci1032_cos_insn_config().
 224 */
 225static int apci1032_cos_cmd(struct comedi_device *dev,
 226                            struct comedi_subdevice *s)
 227{
 228        struct apci1032_private *devpriv = dev->private;
 229
 230        if (!devpriv->ctrl) {
 231                dev_warn(dev->class_dev,
 232                         "Interrupts disabled due to mode configuration!\n");
 233                return -EINVAL;
 234        }
 235
 236        outl(devpriv->mode1, dev->iobase + APCI1032_MODE1_REG);
 237        outl(devpriv->mode2, dev->iobase + APCI1032_MODE2_REG);
 238        outl(devpriv->ctrl, dev->iobase + APCI1032_CTRL_REG);
 239
 240        return 0;
 241}
 242
 243static int apci1032_cos_cancel(struct comedi_device *dev,
 244                               struct comedi_subdevice *s)
 245{
 246        return apci1032_reset(dev);
 247}
 248
 249static irqreturn_t apci1032_interrupt(int irq, void *d)
 250{
 251        struct comedi_device *dev = d;
 252        struct apci1032_private *devpriv = dev->private;
 253        struct comedi_subdevice *s = dev->read_subdev;
 254        unsigned int ctrl;
 255
 256        /* check interrupt is from this device */
 257        if ((inl(devpriv->amcc_iobase + AMCC_OP_REG_INTCSR) &
 258             INTCSR_INTR_ASSERTED) == 0)
 259                return IRQ_NONE;
 260
 261        /* check interrupt is enabled */
 262        ctrl = inl(dev->iobase + APCI1032_CTRL_REG);
 263        if ((ctrl & APCI1032_CTRL_INT_ENA) == 0)
 264                return IRQ_HANDLED;
 265
 266        /* disable the interrupt */
 267        outl(ctrl & ~APCI1032_CTRL_INT_ENA, dev->iobase + APCI1032_CTRL_REG);
 268
 269        s->state = inl(dev->iobase + APCI1032_STATUS_REG) & 0xffff;
 270        comedi_buf_write_samples(s, &s->state, 1);
 271        comedi_handle_events(dev, s);
 272
 273        /* enable the interrupt */
 274        outl(ctrl, dev->iobase + APCI1032_CTRL_REG);
 275
 276        return IRQ_HANDLED;
 277}
 278
 279static int apci1032_di_insn_bits(struct comedi_device *dev,
 280                                 struct comedi_subdevice *s,
 281                                 struct comedi_insn *insn,
 282                                 unsigned int *data)
 283{
 284        data[1] = inl(dev->iobase + APCI1032_DI_REG);
 285
 286        return insn->n;
 287}
 288
 289static int apci1032_auto_attach(struct comedi_device *dev,
 290                                unsigned long context_unused)
 291{
 292        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 293        struct apci1032_private *devpriv;
 294        struct comedi_subdevice *s;
 295        int ret;
 296
 297        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 298        if (!devpriv)
 299                return -ENOMEM;
 300
 301        ret = comedi_pci_enable(dev);
 302        if (ret)
 303                return ret;
 304
 305        devpriv->amcc_iobase = pci_resource_start(pcidev, 0);
 306        dev->iobase = pci_resource_start(pcidev, 1);
 307        apci1032_reset(dev);
 308        if (pcidev->irq > 0) {
 309                ret = request_irq(pcidev->irq, apci1032_interrupt, IRQF_SHARED,
 310                                  dev->board_name, dev);
 311                if (ret == 0)
 312                        dev->irq = pcidev->irq;
 313        }
 314
 315        ret = comedi_alloc_subdevices(dev, 2);
 316        if (ret)
 317                return ret;
 318
 319        /*  Allocate and Initialise DI Subdevice Structures */
 320        s = &dev->subdevices[0];
 321        s->type         = COMEDI_SUBD_DI;
 322        s->subdev_flags = SDF_READABLE;
 323        s->n_chan       = 32;
 324        s->maxdata      = 1;
 325        s->range_table  = &range_digital;
 326        s->insn_bits    = apci1032_di_insn_bits;
 327
 328        /* Change-Of-State (COS) interrupt subdevice */
 329        s = &dev->subdevices[1];
 330        if (dev->irq) {
 331                dev->read_subdev = s;
 332                s->type         = COMEDI_SUBD_DI;
 333                s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
 334                s->n_chan       = 1;
 335                s->maxdata      = 1;
 336                s->range_table  = &range_digital;
 337                s->insn_config  = apci1032_cos_insn_config;
 338                s->insn_bits    = apci1032_cos_insn_bits;
 339                s->len_chanlist = 1;
 340                s->do_cmdtest   = apci1032_cos_cmdtest;
 341                s->do_cmd       = apci1032_cos_cmd;
 342                s->cancel       = apci1032_cos_cancel;
 343        } else {
 344                s->type         = COMEDI_SUBD_UNUSED;
 345        }
 346
 347        return 0;
 348}
 349
 350static void apci1032_detach(struct comedi_device *dev)
 351{
 352        if (dev->iobase)
 353                apci1032_reset(dev);
 354        comedi_pci_detach(dev);
 355}
 356
 357static struct comedi_driver apci1032_driver = {
 358        .driver_name    = "addi_apci_1032",
 359        .module         = THIS_MODULE,
 360        .auto_attach    = apci1032_auto_attach,
 361        .detach         = apci1032_detach,
 362};
 363
 364static int apci1032_pci_probe(struct pci_dev *dev,
 365                              const struct pci_device_id *id)
 366{
 367        return comedi_pci_auto_config(dev, &apci1032_driver, id->driver_data);
 368}
 369
 370static const struct pci_device_id apci1032_pci_table[] = {
 371        { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1003) },
 372        { 0 }
 373};
 374MODULE_DEVICE_TABLE(pci, apci1032_pci_table);
 375
 376static struct pci_driver apci1032_pci_driver = {
 377        .name           = "addi_apci_1032",
 378        .id_table       = apci1032_pci_table,
 379        .probe          = apci1032_pci_probe,
 380        .remove         = comedi_pci_auto_unconfig,
 381};
 382module_comedi_pci_driver(apci1032_driver, apci1032_pci_driver);
 383
 384MODULE_AUTHOR("Comedi http://www.comedi.org");
 385MODULE_DESCRIPTION("ADDI-DATA APCI-1032, 32 channel DI boards");
 386MODULE_LICENSE("GPL");
 387