root/drivers/staging/comedi/drivers/addi_apci_2032.c

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

DEFINITIONS

This source file includes following definitions.
  1. apci2032_do_insn_bits
  2. apci2032_int_insn_bits
  3. apci2032_int_stop
  4. apci2032_int_cmdtest
  5. apci2032_int_cmd
  6. apci2032_int_cancel
  7. apci2032_interrupt
  8. apci2032_reset
  9. apci2032_auto_attach
  10. apci2032_detach
  11. apci2032_pci_probe

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * addi_apci_2032.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 #include <linux/module.h>
  17 #include <linux/interrupt.h>
  18 #include <linux/slab.h>
  19 
  20 #include "../comedi_pci.h"
  21 #include "addi_watchdog.h"
  22 
  23 /*
  24  * PCI bar 1 I/O Register map
  25  */
  26 #define APCI2032_DO_REG                 0x00
  27 #define APCI2032_INT_CTRL_REG           0x04
  28 #define APCI2032_INT_CTRL_VCC_ENA       BIT(0)
  29 #define APCI2032_INT_CTRL_CC_ENA        BIT(1)
  30 #define APCI2032_INT_STATUS_REG         0x08
  31 #define APCI2032_INT_STATUS_VCC         BIT(0)
  32 #define APCI2032_INT_STATUS_CC          BIT(1)
  33 #define APCI2032_STATUS_REG             0x0c
  34 #define APCI2032_STATUS_IRQ             BIT(0)
  35 #define APCI2032_WDOG_REG               0x10
  36 
  37 struct apci2032_int_private {
  38         spinlock_t spinlock;            /* protects the following members */
  39         bool active;                    /* an async command is running */
  40         unsigned char enabled_isns;     /* mask of enabled interrupt channels */
  41 };
  42 
  43 static int apci2032_do_insn_bits(struct comedi_device *dev,
  44                                  struct comedi_subdevice *s,
  45                                  struct comedi_insn *insn,
  46                                  unsigned int *data)
  47 {
  48         s->state = inl(dev->iobase + APCI2032_DO_REG);
  49 
  50         if (comedi_dio_update_state(s, data))
  51                 outl(s->state, dev->iobase + APCI2032_DO_REG);
  52 
  53         data[1] = s->state;
  54 
  55         return insn->n;
  56 }
  57 
  58 static int apci2032_int_insn_bits(struct comedi_device *dev,
  59                                   struct comedi_subdevice *s,
  60                                   struct comedi_insn *insn,
  61                                   unsigned int *data)
  62 {
  63         data[1] = inl(dev->iobase + APCI2032_INT_STATUS_REG) & 3;
  64         return insn->n;
  65 }
  66 
  67 static void apci2032_int_stop(struct comedi_device *dev,
  68                               struct comedi_subdevice *s)
  69 {
  70         struct apci2032_int_private *subpriv = s->private;
  71 
  72         subpriv->active = false;
  73         subpriv->enabled_isns = 0;
  74         outl(0x0, dev->iobase + APCI2032_INT_CTRL_REG);
  75 }
  76 
  77 static int apci2032_int_cmdtest(struct comedi_device *dev,
  78                                 struct comedi_subdevice *s,
  79                                 struct comedi_cmd *cmd)
  80 {
  81         int err = 0;
  82 
  83         /* Step 1 : check if triggers are trivially valid */
  84 
  85         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
  86         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
  87         err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
  88         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
  89         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
  90 
  91         if (err)
  92                 return 1;
  93 
  94         /* Step 2a : make sure trigger sources are unique */
  95         err |= comedi_check_trigger_is_unique(cmd->stop_src);
  96 
  97         /* Step 2b : and mutually compatible */
  98 
  99         if (err)
 100                 return 2;
 101 
 102         /* Step 3: check if arguments are trivially valid */
 103 
 104         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 105         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 106         err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 107         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 108                                            cmd->chanlist_len);
 109         if (cmd->stop_src == TRIG_COUNT)
 110                 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 111         else    /* TRIG_NONE */
 112                 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 113 
 114         if (err)
 115                 return 3;
 116 
 117         /* Step 4: fix up any arguments */
 118 
 119         /* Step 5: check channel list if it exists */
 120 
 121         return 0;
 122 }
 123 
 124 static int apci2032_int_cmd(struct comedi_device *dev,
 125                             struct comedi_subdevice *s)
 126 {
 127         struct comedi_cmd *cmd = &s->async->cmd;
 128         struct apci2032_int_private *subpriv = s->private;
 129         unsigned char enabled_isns;
 130         unsigned int n;
 131         unsigned long flags;
 132 
 133         enabled_isns = 0;
 134         for (n = 0; n < cmd->chanlist_len; n++)
 135                 enabled_isns |= 1 << CR_CHAN(cmd->chanlist[n]);
 136 
 137         spin_lock_irqsave(&subpriv->spinlock, flags);
 138 
 139         subpriv->enabled_isns = enabled_isns;
 140         subpriv->active = true;
 141         outl(enabled_isns, dev->iobase + APCI2032_INT_CTRL_REG);
 142 
 143         spin_unlock_irqrestore(&subpriv->spinlock, flags);
 144 
 145         return 0;
 146 }
 147 
 148 static int apci2032_int_cancel(struct comedi_device *dev,
 149                                struct comedi_subdevice *s)
 150 {
 151         struct apci2032_int_private *subpriv = s->private;
 152         unsigned long flags;
 153 
 154         spin_lock_irqsave(&subpriv->spinlock, flags);
 155         if (subpriv->active)
 156                 apci2032_int_stop(dev, s);
 157         spin_unlock_irqrestore(&subpriv->spinlock, flags);
 158 
 159         return 0;
 160 }
 161 
 162 static irqreturn_t apci2032_interrupt(int irq, void *d)
 163 {
 164         struct comedi_device *dev = d;
 165         struct comedi_subdevice *s = dev->read_subdev;
 166         struct comedi_cmd *cmd = &s->async->cmd;
 167         struct apci2032_int_private *subpriv;
 168         unsigned int val;
 169 
 170         if (!dev->attached)
 171                 return IRQ_NONE;
 172 
 173         /* Check if VCC OR CC interrupt has occurred */
 174         val = inl(dev->iobase + APCI2032_STATUS_REG) & APCI2032_STATUS_IRQ;
 175         if (!val)
 176                 return IRQ_NONE;
 177 
 178         subpriv = s->private;
 179         spin_lock(&subpriv->spinlock);
 180 
 181         val = inl(dev->iobase + APCI2032_INT_STATUS_REG) & 3;
 182         /* Disable triggered interrupt sources. */
 183         outl(~val & 3, dev->iobase + APCI2032_INT_CTRL_REG);
 184         /*
 185          * Note: We don't reenable the triggered interrupt sources because they
 186          * are level-sensitive, hardware error status interrupt sources and
 187          * they'd keep triggering interrupts repeatedly.
 188          */
 189 
 190         if (subpriv->active && (val & subpriv->enabled_isns) != 0) {
 191                 unsigned short bits = 0;
 192                 int i;
 193 
 194                 /* Bits in scan data correspond to indices in channel list. */
 195                 for (i = 0; i < cmd->chanlist_len; i++) {
 196                         unsigned int chan = CR_CHAN(cmd->chanlist[i]);
 197 
 198                         if (val & (1 << chan))
 199                                 bits |= (1 << i);
 200                 }
 201 
 202                 comedi_buf_write_samples(s, &bits, 1);
 203 
 204                 if (cmd->stop_src == TRIG_COUNT &&
 205                     s->async->scans_done >= cmd->stop_arg)
 206                         s->async->events |= COMEDI_CB_EOA;
 207         }
 208 
 209         spin_unlock(&subpriv->spinlock);
 210 
 211         comedi_handle_events(dev, s);
 212 
 213         return IRQ_HANDLED;
 214 }
 215 
 216 static int apci2032_reset(struct comedi_device *dev)
 217 {
 218         outl(0x0, dev->iobase + APCI2032_DO_REG);
 219         outl(0x0, dev->iobase + APCI2032_INT_CTRL_REG);
 220 
 221         addi_watchdog_reset(dev->iobase + APCI2032_WDOG_REG);
 222 
 223         return 0;
 224 }
 225 
 226 static int apci2032_auto_attach(struct comedi_device *dev,
 227                                 unsigned long context_unused)
 228 {
 229         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 230         struct comedi_subdevice *s;
 231         int ret;
 232 
 233         ret = comedi_pci_enable(dev);
 234         if (ret)
 235                 return ret;
 236         dev->iobase = pci_resource_start(pcidev, 1);
 237         apci2032_reset(dev);
 238 
 239         if (pcidev->irq > 0) {
 240                 ret = request_irq(pcidev->irq, apci2032_interrupt,
 241                                   IRQF_SHARED, dev->board_name, dev);
 242                 if (ret == 0)
 243                         dev->irq = pcidev->irq;
 244         }
 245 
 246         ret = comedi_alloc_subdevices(dev, 3);
 247         if (ret)
 248                 return ret;
 249 
 250         /* Initialize the digital output subdevice */
 251         s = &dev->subdevices[0];
 252         s->type         = COMEDI_SUBD_DO;
 253         s->subdev_flags = SDF_WRITABLE;
 254         s->n_chan       = 32;
 255         s->maxdata      = 1;
 256         s->range_table  = &range_digital;
 257         s->insn_bits    = apci2032_do_insn_bits;
 258 
 259         /* Initialize the watchdog subdevice */
 260         s = &dev->subdevices[1];
 261         ret = addi_watchdog_init(s, dev->iobase + APCI2032_WDOG_REG);
 262         if (ret)
 263                 return ret;
 264 
 265         /* Initialize the interrupt subdevice */
 266         s = &dev->subdevices[2];
 267         s->type         = COMEDI_SUBD_DI;
 268         s->subdev_flags = SDF_READABLE;
 269         s->n_chan       = 2;
 270         s->maxdata      = 1;
 271         s->range_table  = &range_digital;
 272         s->insn_bits    = apci2032_int_insn_bits;
 273         if (dev->irq) {
 274                 struct apci2032_int_private *subpriv;
 275 
 276                 dev->read_subdev = s;
 277                 subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL);
 278                 if (!subpriv)
 279                         return -ENOMEM;
 280                 spin_lock_init(&subpriv->spinlock);
 281                 s->private      = subpriv;
 282                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_PACKED;
 283                 s->len_chanlist = 2;
 284                 s->do_cmdtest   = apci2032_int_cmdtest;
 285                 s->do_cmd       = apci2032_int_cmd;
 286                 s->cancel       = apci2032_int_cancel;
 287         }
 288 
 289         return 0;
 290 }
 291 
 292 static void apci2032_detach(struct comedi_device *dev)
 293 {
 294         if (dev->iobase)
 295                 apci2032_reset(dev);
 296         comedi_pci_detach(dev);
 297         if (dev->read_subdev)
 298                 kfree(dev->read_subdev->private);
 299 }
 300 
 301 static struct comedi_driver apci2032_driver = {
 302         .driver_name    = "addi_apci_2032",
 303         .module         = THIS_MODULE,
 304         .auto_attach    = apci2032_auto_attach,
 305         .detach         = apci2032_detach,
 306 };
 307 
 308 static int apci2032_pci_probe(struct pci_dev *dev,
 309                               const struct pci_device_id *id)
 310 {
 311         return comedi_pci_auto_config(dev, &apci2032_driver, id->driver_data);
 312 }
 313 
 314 static const struct pci_device_id apci2032_pci_table[] = {
 315         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1004) },
 316         { 0 }
 317 };
 318 MODULE_DEVICE_TABLE(pci, apci2032_pci_table);
 319 
 320 static struct pci_driver apci2032_pci_driver = {
 321         .name           = "addi_apci_2032",
 322         .id_table       = apci2032_pci_table,
 323         .probe          = apci2032_pci_probe,
 324         .remove         = comedi_pci_auto_unconfig,
 325 };
 326 module_comedi_pci_driver(apci2032_driver, apci2032_pci_driver);
 327 
 328 MODULE_AUTHOR("Comedi http://www.comedi.org");
 329 MODULE_DESCRIPTION("ADDI-DATA APCI-2032, 32 channel DO boards");
 330 MODULE_LICENSE("GPL");

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