root/drivers/staging/comedi/drivers/ni_6527.c

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

DEFINITIONS

This source file includes following definitions.
  1. ni6527_set_filter_interval
  2. ni6527_set_filter_enable
  3. ni6527_di_insn_config
  4. ni6527_di_insn_bits
  5. ni6527_do_insn_bits
  6. ni6527_interrupt
  7. ni6527_intr_cmdtest
  8. ni6527_intr_cmd
  9. ni6527_intr_cancel
  10. ni6527_intr_insn_bits
  11. ni6527_set_edge_detection
  12. ni6527_intr_insn_config
  13. ni6527_reset
  14. ni6527_auto_attach
  15. ni6527_detach
  16. ni6527_pci_probe

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * ni_6527.c
   4  * Comedi driver for National Instruments PCI-6527
   5  *
   6  * COMEDI - Linux Control and Measurement Device Interface
   7  * Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
   8  */
   9 
  10 /*
  11  * Driver: ni_6527
  12  * Description: National Instruments 6527
  13  * Devices: [National Instruments] PCI-6527 (pci-6527), PXI-6527 (pxi-6527)
  14  * Author: David A. Schleef <ds@schleef.org>
  15  * Updated: Sat, 25 Jan 2003 13:24:40 -0800
  16  * Status: works
  17  *
  18  * Configuration Options: not applicable, uses PCI auto config
  19  */
  20 
  21 #include <linux/module.h>
  22 #include <linux/interrupt.h>
  23 
  24 #include "../comedi_pci.h"
  25 
  26 /*
  27  * PCI BAR1 - Register memory map
  28  *
  29  * Manuals (available from ftp://ftp.natinst.com/support/manuals)
  30  *      370106b.pdf     6527 Register Level Programmer Manual
  31  */
  32 #define NI6527_DI_REG(x)                (0x00 + (x))
  33 #define NI6527_DO_REG(x)                (0x03 + (x))
  34 #define NI6527_ID_REG                   0x06
  35 #define NI6527_CLR_REG                  0x07
  36 #define NI6527_CLR_EDGE                 BIT(3)
  37 #define NI6527_CLR_OVERFLOW             BIT(2)
  38 #define NI6527_CLR_FILT                 BIT(1)
  39 #define NI6527_CLR_INTERVAL             BIT(0)
  40 #define NI6527_CLR_IRQS                 (NI6527_CLR_EDGE | NI6527_CLR_OVERFLOW)
  41 #define NI6527_CLR_RESET_FILT           (NI6527_CLR_FILT | NI6527_CLR_INTERVAL)
  42 #define NI6527_FILT_INTERVAL_REG(x)     (0x08 + (x))
  43 #define NI6527_FILT_ENA_REG(x)          (0x0c + (x))
  44 #define NI6527_STATUS_REG               0x14
  45 #define NI6527_STATUS_IRQ               BIT(2)
  46 #define NI6527_STATUS_OVERFLOW          BIT(1)
  47 #define NI6527_STATUS_EDGE              BIT(0)
  48 #define NI6527_CTRL_REG                 0x15
  49 #define NI6527_CTRL_FALLING             BIT(4)
  50 #define NI6527_CTRL_RISING              BIT(3)
  51 #define NI6527_CTRL_IRQ                 BIT(2)
  52 #define NI6527_CTRL_OVERFLOW            BIT(1)
  53 #define NI6527_CTRL_EDGE                BIT(0)
  54 #define NI6527_CTRL_DISABLE_IRQS        0
  55 #define NI6527_CTRL_ENABLE_IRQS         (NI6527_CTRL_FALLING | \
  56                                          NI6527_CTRL_RISING | \
  57                                          NI6527_CTRL_IRQ | NI6527_CTRL_EDGE)
  58 #define NI6527_RISING_EDGE_REG(x)       (0x18 + (x))
  59 #define NI6527_FALLING_EDGE_REG(x)      (0x20 + (x))
  60 
  61 enum ni6527_boardid {
  62         BOARD_PCI6527,
  63         BOARD_PXI6527,
  64 };
  65 
  66 struct ni6527_board {
  67         const char *name;
  68 };
  69 
  70 static const struct ni6527_board ni6527_boards[] = {
  71         [BOARD_PCI6527] = {
  72                 .name           = "pci-6527",
  73         },
  74         [BOARD_PXI6527] = {
  75                 .name           = "pxi-6527",
  76         },
  77 };
  78 
  79 struct ni6527_private {
  80         unsigned int filter_interval;
  81         unsigned int filter_enable;
  82 };
  83 
  84 static void ni6527_set_filter_interval(struct comedi_device *dev,
  85                                        unsigned int val)
  86 {
  87         struct ni6527_private *devpriv = dev->private;
  88 
  89         if (val != devpriv->filter_interval) {
  90                 writeb(val & 0xff, dev->mmio + NI6527_FILT_INTERVAL_REG(0));
  91                 writeb((val >> 8) & 0xff,
  92                        dev->mmio + NI6527_FILT_INTERVAL_REG(1));
  93                 writeb((val >> 16) & 0x0f,
  94                        dev->mmio + NI6527_FILT_INTERVAL_REG(2));
  95 
  96                 writeb(NI6527_CLR_INTERVAL, dev->mmio + NI6527_CLR_REG);
  97 
  98                 devpriv->filter_interval = val;
  99         }
 100 }
 101 
 102 static void ni6527_set_filter_enable(struct comedi_device *dev,
 103                                      unsigned int val)
 104 {
 105         writeb(val & 0xff, dev->mmio + NI6527_FILT_ENA_REG(0));
 106         writeb((val >> 8) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(1));
 107         writeb((val >> 16) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(2));
 108 }
 109 
 110 static int ni6527_di_insn_config(struct comedi_device *dev,
 111                                  struct comedi_subdevice *s,
 112                                  struct comedi_insn *insn,
 113                                  unsigned int *data)
 114 {
 115         struct ni6527_private *devpriv = dev->private;
 116         unsigned int chan = CR_CHAN(insn->chanspec);
 117         unsigned int interval;
 118 
 119         switch (data[0]) {
 120         case INSN_CONFIG_FILTER:
 121                 /*
 122                  * The deglitch filter interval is specified in nanoseconds.
 123                  * The hardware supports intervals in 200ns increments. Round
 124                  * the user values up and return the actual interval.
 125                  */
 126                 interval = (data[1] + 100) / 200;
 127                 data[1] = interval * 200;
 128 
 129                 if (interval) {
 130                         ni6527_set_filter_interval(dev, interval);
 131                         devpriv->filter_enable |= 1 << chan;
 132                 } else {
 133                         devpriv->filter_enable &= ~(1 << chan);
 134                 }
 135                 ni6527_set_filter_enable(dev, devpriv->filter_enable);
 136                 break;
 137         default:
 138                 return -EINVAL;
 139         }
 140 
 141         return insn->n;
 142 }
 143 
 144 static int ni6527_di_insn_bits(struct comedi_device *dev,
 145                                struct comedi_subdevice *s,
 146                                struct comedi_insn *insn,
 147                                unsigned int *data)
 148 {
 149         unsigned int val;
 150 
 151         val = readb(dev->mmio + NI6527_DI_REG(0));
 152         val |= (readb(dev->mmio + NI6527_DI_REG(1)) << 8);
 153         val |= (readb(dev->mmio + NI6527_DI_REG(2)) << 16);
 154 
 155         data[1] = val;
 156 
 157         return insn->n;
 158 }
 159 
 160 static int ni6527_do_insn_bits(struct comedi_device *dev,
 161                                struct comedi_subdevice *s,
 162                                struct comedi_insn *insn,
 163                                unsigned int *data)
 164 {
 165         unsigned int mask;
 166 
 167         mask = comedi_dio_update_state(s, data);
 168         if (mask) {
 169                 /* Outputs are inverted */
 170                 unsigned int val = s->state ^ 0xffffff;
 171 
 172                 if (mask & 0x0000ff)
 173                         writeb(val & 0xff, dev->mmio + NI6527_DO_REG(0));
 174                 if (mask & 0x00ff00)
 175                         writeb((val >> 8) & 0xff,
 176                                dev->mmio + NI6527_DO_REG(1));
 177                 if (mask & 0xff0000)
 178                         writeb((val >> 16) & 0xff,
 179                                dev->mmio + NI6527_DO_REG(2));
 180         }
 181 
 182         data[1] = s->state;
 183 
 184         return insn->n;
 185 }
 186 
 187 static irqreturn_t ni6527_interrupt(int irq, void *d)
 188 {
 189         struct comedi_device *dev = d;
 190         struct comedi_subdevice *s = dev->read_subdev;
 191         unsigned int status;
 192 
 193         status = readb(dev->mmio + NI6527_STATUS_REG);
 194         if (!(status & NI6527_STATUS_IRQ))
 195                 return IRQ_NONE;
 196 
 197         if (status & NI6527_STATUS_EDGE) {
 198                 comedi_buf_write_samples(s, &s->state, 1);
 199                 comedi_handle_events(dev, s);
 200         }
 201 
 202         writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG);
 203 
 204         return IRQ_HANDLED;
 205 }
 206 
 207 static int ni6527_intr_cmdtest(struct comedi_device *dev,
 208                                struct comedi_subdevice *s,
 209                                struct comedi_cmd *cmd)
 210 {
 211         int err = 0;
 212 
 213         /* Step 1 : check if triggers are trivially valid */
 214 
 215         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 216         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_OTHER);
 217         err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
 218         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 219         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
 220 
 221         if (err)
 222                 return 1;
 223 
 224         /* Step 2a : make sure trigger sources are unique */
 225         /* Step 2b : and mutually compatible */
 226 
 227         /* Step 3: check if arguments are trivially valid */
 228 
 229         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 230         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 231         err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 232         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 233                                            cmd->chanlist_len);
 234         err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 235 
 236         if (err)
 237                 return 3;
 238 
 239         /* Step 4: fix up any arguments */
 240 
 241         /* Step 5: check channel list if it exists */
 242 
 243         return 0;
 244 }
 245 
 246 static int ni6527_intr_cmd(struct comedi_device *dev,
 247                            struct comedi_subdevice *s)
 248 {
 249         writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG);
 250         writeb(NI6527_CTRL_ENABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
 251 
 252         return 0;
 253 }
 254 
 255 static int ni6527_intr_cancel(struct comedi_device *dev,
 256                               struct comedi_subdevice *s)
 257 {
 258         writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
 259 
 260         return 0;
 261 }
 262 
 263 static int ni6527_intr_insn_bits(struct comedi_device *dev,
 264                                  struct comedi_subdevice *s,
 265                                  struct comedi_insn *insn, unsigned int *data)
 266 {
 267         data[1] = 0;
 268         return insn->n;
 269 }
 270 
 271 static void ni6527_set_edge_detection(struct comedi_device *dev,
 272                                       unsigned int mask,
 273                                       unsigned int rising,
 274                                       unsigned int falling)
 275 {
 276         unsigned int i;
 277 
 278         rising &= mask;
 279         falling &= mask;
 280         for (i = 0; i < 2; i++) {
 281                 if (mask & 0xff) {
 282                         if (~mask & 0xff) {
 283                                 /* preserve rising-edge detection channels */
 284                                 rising |= readb(dev->mmio +
 285                                                 NI6527_RISING_EDGE_REG(i)) &
 286                                           (~mask & 0xff);
 287                                 /* preserve falling-edge detection channels */
 288                                 falling |= readb(dev->mmio +
 289                                                  NI6527_FALLING_EDGE_REG(i)) &
 290                                            (~mask & 0xff);
 291                         }
 292                         /* update rising-edge detection channels */
 293                         writeb(rising & 0xff,
 294                                dev->mmio + NI6527_RISING_EDGE_REG(i));
 295                         /* update falling-edge detection channels */
 296                         writeb(falling & 0xff,
 297                                dev->mmio + NI6527_FALLING_EDGE_REG(i));
 298                 }
 299                 rising >>= 8;
 300                 falling >>= 8;
 301                 mask >>= 8;
 302         }
 303 }
 304 
 305 static int ni6527_intr_insn_config(struct comedi_device *dev,
 306                                    struct comedi_subdevice *s,
 307                                    struct comedi_insn *insn,
 308                                    unsigned int *data)
 309 {
 310         unsigned int mask = 0xffffffff;
 311         unsigned int rising, falling, shift;
 312 
 313         switch (data[0]) {
 314         case INSN_CONFIG_CHANGE_NOTIFY:
 315                 /* check_insn_config_length() does not check this instruction */
 316                 if (insn->n != 3)
 317                         return -EINVAL;
 318                 rising = data[1];
 319                 falling = data[2];
 320                 ni6527_set_edge_detection(dev, mask, rising, falling);
 321                 break;
 322         case INSN_CONFIG_DIGITAL_TRIG:
 323                 /* check trigger number */
 324                 if (data[1] != 0)
 325                         return -EINVAL;
 326                 /* check digital trigger operation */
 327                 switch (data[2]) {
 328                 case COMEDI_DIGITAL_TRIG_DISABLE:
 329                         rising = 0;
 330                         falling = 0;
 331                         break;
 332                 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
 333                         /* check shift amount */
 334                         shift = data[3];
 335                         if (shift >= s->n_chan) {
 336                                 mask = 0;
 337                                 rising = 0;
 338                                 falling = 0;
 339                         } else {
 340                                 mask <<= shift;
 341                                 rising = data[4] << shift;
 342                                 falling = data[5] << shift;
 343                         }
 344                         break;
 345                 default:
 346                         return -EINVAL;
 347                 }
 348                 ni6527_set_edge_detection(dev, mask, rising, falling);
 349                 break;
 350         default:
 351                 return -EINVAL;
 352         }
 353 
 354         return insn->n;
 355 }
 356 
 357 static void ni6527_reset(struct comedi_device *dev)
 358 {
 359         /* disable deglitch filters on all channels */
 360         ni6527_set_filter_enable(dev, 0);
 361 
 362         /* disable edge detection */
 363         ni6527_set_edge_detection(dev, 0xffffffff, 0, 0);
 364 
 365         writeb(NI6527_CLR_IRQS | NI6527_CLR_RESET_FILT,
 366                dev->mmio + NI6527_CLR_REG);
 367         writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
 368 }
 369 
 370 static int ni6527_auto_attach(struct comedi_device *dev,
 371                               unsigned long context)
 372 {
 373         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 374         const struct ni6527_board *board = NULL;
 375         struct ni6527_private *devpriv;
 376         struct comedi_subdevice *s;
 377         int ret;
 378 
 379         if (context < ARRAY_SIZE(ni6527_boards))
 380                 board = &ni6527_boards[context];
 381         if (!board)
 382                 return -ENODEV;
 383         dev->board_ptr = board;
 384         dev->board_name = board->name;
 385 
 386         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 387         if (!devpriv)
 388                 return -ENOMEM;
 389 
 390         ret = comedi_pci_enable(dev);
 391         if (ret)
 392                 return ret;
 393 
 394         dev->mmio = pci_ioremap_bar(pcidev, 1);
 395         if (!dev->mmio)
 396                 return -ENOMEM;
 397 
 398         /* make sure this is actually a 6527 device */
 399         if (readb(dev->mmio + NI6527_ID_REG) != 0x27)
 400                 return -ENODEV;
 401 
 402         ni6527_reset(dev);
 403 
 404         ret = request_irq(pcidev->irq, ni6527_interrupt, IRQF_SHARED,
 405                           dev->board_name, dev);
 406         if (ret == 0)
 407                 dev->irq = pcidev->irq;
 408 
 409         ret = comedi_alloc_subdevices(dev, 3);
 410         if (ret)
 411                 return ret;
 412 
 413         /* Digital Input subdevice */
 414         s = &dev->subdevices[0];
 415         s->type         = COMEDI_SUBD_DI;
 416         s->subdev_flags = SDF_READABLE;
 417         s->n_chan       = 24;
 418         s->maxdata      = 1;
 419         s->range_table  = &range_digital;
 420         s->insn_config  = ni6527_di_insn_config;
 421         s->insn_bits    = ni6527_di_insn_bits;
 422 
 423         /* Digital Output subdevice */
 424         s = &dev->subdevices[1];
 425         s->type         = COMEDI_SUBD_DO;
 426         s->subdev_flags = SDF_WRITABLE;
 427         s->n_chan       = 24;
 428         s->maxdata      = 1;
 429         s->range_table  = &range_digital;
 430         s->insn_bits    = ni6527_do_insn_bits;
 431 
 432         /* Edge detection interrupt subdevice */
 433         s = &dev->subdevices[2];
 434         if (dev->irq) {
 435                 dev->read_subdev = s;
 436                 s->type         = COMEDI_SUBD_DI;
 437                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
 438                 s->n_chan       = 1;
 439                 s->maxdata      = 1;
 440                 s->range_table  = &range_digital;
 441                 s->insn_config  = ni6527_intr_insn_config;
 442                 s->insn_bits    = ni6527_intr_insn_bits;
 443                 s->len_chanlist = 1;
 444                 s->do_cmdtest   = ni6527_intr_cmdtest;
 445                 s->do_cmd       = ni6527_intr_cmd;
 446                 s->cancel       = ni6527_intr_cancel;
 447         } else {
 448                 s->type = COMEDI_SUBD_UNUSED;
 449         }
 450 
 451         return 0;
 452 }
 453 
 454 static void ni6527_detach(struct comedi_device *dev)
 455 {
 456         if (dev->mmio)
 457                 ni6527_reset(dev);
 458         comedi_pci_detach(dev);
 459 }
 460 
 461 static struct comedi_driver ni6527_driver = {
 462         .driver_name    = "ni_6527",
 463         .module         = THIS_MODULE,
 464         .auto_attach    = ni6527_auto_attach,
 465         .detach         = ni6527_detach,
 466 };
 467 
 468 static int ni6527_pci_probe(struct pci_dev *dev,
 469                             const struct pci_device_id *id)
 470 {
 471         return comedi_pci_auto_config(dev, &ni6527_driver, id->driver_data);
 472 }
 473 
 474 static const struct pci_device_id ni6527_pci_table[] = {
 475         { PCI_VDEVICE(NI, 0x2b10), BOARD_PXI6527 },
 476         { PCI_VDEVICE(NI, 0x2b20), BOARD_PCI6527 },
 477         { 0 }
 478 };
 479 MODULE_DEVICE_TABLE(pci, ni6527_pci_table);
 480 
 481 static struct pci_driver ni6527_pci_driver = {
 482         .name           = "ni_6527",
 483         .id_table       = ni6527_pci_table,
 484         .probe          = ni6527_pci_probe,
 485         .remove         = comedi_pci_auto_unconfig,
 486 };
 487 module_comedi_pci_driver(ni6527_driver, ni6527_pci_driver);
 488 
 489 MODULE_AUTHOR("Comedi http://www.comedi.org");
 490 MODULE_DESCRIPTION("Comedi driver for National Instruments PCI-6527");
 491 MODULE_LICENSE("GPL");

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