root/drivers/staging/comedi/drivers/addi_apci_1032.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. apci1032_reset
  2. apci1032_cos_insn_config
  3. apci1032_cos_insn_bits
  4. apci1032_cos_cmdtest
  5. apci1032_cos_cmd
  6. apci1032_cos_cancel
  7. apci1032_interrupt
  8. apci1032_di_insn_bits
  9. apci1032_auto_attach
  10. apci1032_detach
  11. apci1032_pci_probe

   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 
  83 struct 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 
  90 static 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 
 103 static 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 
 171 static 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 
 181 static 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  */
 225 static 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 
 243 static int apci1032_cos_cancel(struct comedi_device *dev,
 244                                struct comedi_subdevice *s)
 245 {
 246         return apci1032_reset(dev);
 247 }
 248 
 249 static 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 
 279 static 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 
 289 static 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 
 350 static void apci1032_detach(struct comedi_device *dev)
 351 {
 352         if (dev->iobase)
 353                 apci1032_reset(dev);
 354         comedi_pci_detach(dev);
 355 }
 356 
 357 static 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 
 364 static 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 
 370 static const struct pci_device_id apci1032_pci_table[] = {
 371         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1003) },
 372         { 0 }
 373 };
 374 MODULE_DEVICE_TABLE(pci, apci1032_pci_table);
 375 
 376 static 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 };
 382 module_comedi_pci_driver(apci1032_driver, apci1032_pci_driver);
 383 
 384 MODULE_AUTHOR("Comedi http://www.comedi.org");
 385 MODULE_DESCRIPTION("ADDI-DATA APCI-1032, 32 channel DI boards");
 386 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */