linux/drivers/staging/comedi/drivers/ni_tiocmd.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Command support for NI general purpose counters
   4 *
   5 * Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net>
   6 */
   7
   8/*
   9 * Module: ni_tiocmd
  10 * Description: National Instruments general purpose counters command support
  11 * Author: J.P. Mellor <jpmellor@rose-hulman.edu>,
  12 *         Herman.Bruyninckx@mech.kuleuven.ac.be,
  13 *         Wim.Meeussen@mech.kuleuven.ac.be,
  14 *         Klaas.Gadeyne@mech.kuleuven.ac.be,
  15 *         Frank Mori Hess <fmhess@users.sourceforge.net>
  16 * Updated: Fri, 11 Apr 2008 12:32:35 +0100
  17 * Status: works
  18 *
  19 * This module is not used directly by end-users.  Rather, it
  20 * is used by other drivers (for example ni_660x and ni_pcimio)
  21 * to provide command support for NI's general purpose counters.
  22 * It was originally split out of ni_tio.c to stop the 'ni_tio'
  23 * module depending on the 'mite' module.
  24 *
  25 * References:
  26 * DAQ 660x Register-Level Programmer Manual  (NI 370505A-01)
  27 * DAQ 6601/6602 User Manual (NI 322137B-01)
  28 * 340934b.pdf  DAQ-STC reference manual
  29 *
  30 * TODO: Support use of both banks X and Y
  31 */
  32
  33#include <linux/module.h>
  34#include "ni_tio_internal.h"
  35#include "mite.h"
  36
  37static void ni_tio_configure_dma(struct ni_gpct *counter,
  38                                 bool enable, bool read)
  39{
  40        struct ni_gpct_device *counter_dev = counter->counter_dev;
  41        unsigned int cidx = counter->counter_index;
  42        unsigned int mask;
  43        unsigned int bits;
  44
  45        mask = GI_READ_ACKS_IRQ | GI_WRITE_ACKS_IRQ;
  46        bits = 0;
  47
  48        if (enable) {
  49                if (read)
  50                        bits |= GI_READ_ACKS_IRQ;
  51                else
  52                        bits |= GI_WRITE_ACKS_IRQ;
  53        }
  54        ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), mask, bits);
  55
  56        switch (counter_dev->variant) {
  57        case ni_gpct_variant_e_series:
  58                break;
  59        case ni_gpct_variant_m_series:
  60        case ni_gpct_variant_660x:
  61                mask = GI_DMA_ENABLE | GI_DMA_INT_ENA | GI_DMA_WRITE;
  62                bits = 0;
  63
  64                if (enable)
  65                        bits |= GI_DMA_ENABLE | GI_DMA_INT_ENA;
  66                if (!read)
  67                        bits |= GI_DMA_WRITE;
  68                ni_tio_set_bits(counter, NITIO_DMA_CFG_REG(cidx), mask, bits);
  69                break;
  70        }
  71}
  72
  73static int ni_tio_input_inttrig(struct comedi_device *dev,
  74                                struct comedi_subdevice *s,
  75                                unsigned int trig_num)
  76{
  77        struct ni_gpct *counter = s->private;
  78        struct comedi_cmd *cmd = &s->async->cmd;
  79        unsigned long flags;
  80        int ret = 0;
  81
  82        if (trig_num != cmd->start_arg)
  83                return -EINVAL;
  84
  85        spin_lock_irqsave(&counter->lock, flags);
  86        if (counter->mite_chan)
  87                mite_dma_arm(counter->mite_chan);
  88        else
  89                ret = -EIO;
  90        spin_unlock_irqrestore(&counter->lock, flags);
  91        if (ret < 0)
  92                return ret;
  93        ret = ni_tio_arm(counter, true, NI_GPCT_ARM_IMMEDIATE);
  94        s->async->inttrig = NULL;
  95
  96        return ret;
  97}
  98
  99static int ni_tio_input_cmd(struct comedi_subdevice *s)
 100{
 101        struct ni_gpct *counter = s->private;
 102        struct ni_gpct_device *counter_dev = counter->counter_dev;
 103        unsigned int cidx = counter->counter_index;
 104        struct comedi_async *async = s->async;
 105        struct comedi_cmd *cmd = &async->cmd;
 106        int ret = 0;
 107
 108        /* write alloc the entire buffer */
 109        comedi_buf_write_alloc(s, async->prealloc_bufsz);
 110        counter->mite_chan->dir = COMEDI_INPUT;
 111        switch (counter_dev->variant) {
 112        case ni_gpct_variant_m_series:
 113        case ni_gpct_variant_660x:
 114                mite_prep_dma(counter->mite_chan, 32, 32);
 115                break;
 116        case ni_gpct_variant_e_series:
 117                mite_prep_dma(counter->mite_chan, 16, 32);
 118                break;
 119        }
 120        ni_tio_set_bits(counter, NITIO_CMD_REG(cidx), GI_SAVE_TRACE, 0);
 121        ni_tio_configure_dma(counter, true, true);
 122
 123        if (cmd->start_src == TRIG_INT) {
 124                async->inttrig = &ni_tio_input_inttrig;
 125        } else {        /* TRIG_NOW || TRIG_EXT || TRIG_OTHER */
 126                async->inttrig = NULL;
 127                mite_dma_arm(counter->mite_chan);
 128
 129                if (cmd->start_src == TRIG_NOW)
 130                        ret = ni_tio_arm(counter, true, NI_GPCT_ARM_IMMEDIATE);
 131                else if (cmd->start_src == TRIG_EXT)
 132                        ret = ni_tio_arm(counter, true, cmd->start_arg);
 133        }
 134        return ret;
 135}
 136
 137static int ni_tio_output_cmd(struct comedi_subdevice *s)
 138{
 139        struct ni_gpct *counter = s->private;
 140
 141        dev_err(counter->counter_dev->dev->class_dev,
 142                "output commands not yet implemented.\n");
 143        return -ENOTSUPP;
 144}
 145
 146static int ni_tio_cmd_setup(struct comedi_subdevice *s)
 147{
 148        struct comedi_cmd *cmd = &s->async->cmd;
 149        struct ni_gpct *counter = s->private;
 150        unsigned int cidx = counter->counter_index;
 151        int set_gate_source = 0;
 152        unsigned int gate_source;
 153        int retval = 0;
 154
 155        if (cmd->scan_begin_src == TRIG_EXT) {
 156                set_gate_source = 1;
 157                gate_source = cmd->scan_begin_arg;
 158        } else if (cmd->convert_src == TRIG_EXT) {
 159                set_gate_source = 1;
 160                gate_source = cmd->convert_arg;
 161        }
 162        if (set_gate_source)
 163                retval = ni_tio_set_gate_src(counter, 0, gate_source);
 164        if (cmd->flags & CMDF_WAKE_EOS) {
 165                ni_tio_set_bits(counter, NITIO_INT_ENA_REG(cidx),
 166                                GI_GATE_INTERRUPT_ENABLE(cidx),
 167                                GI_GATE_INTERRUPT_ENABLE(cidx));
 168        }
 169        return retval;
 170}
 171
 172int ni_tio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 173{
 174        struct ni_gpct *counter = s->private;
 175        struct comedi_async *async = s->async;
 176        struct comedi_cmd *cmd = &async->cmd;
 177        int retval = 0;
 178        unsigned long flags;
 179
 180        spin_lock_irqsave(&counter->lock, flags);
 181        if (!counter->mite_chan) {
 182                dev_err(counter->counter_dev->dev->class_dev,
 183                        "commands only supported with DMA.  ");
 184                dev_err(counter->counter_dev->dev->class_dev,
 185                        "Interrupt-driven commands not yet implemented.\n");
 186                retval = -EIO;
 187        } else {
 188                retval = ni_tio_cmd_setup(s);
 189                if (retval == 0) {
 190                        if (cmd->flags & CMDF_WRITE)
 191                                retval = ni_tio_output_cmd(s);
 192                        else
 193                                retval = ni_tio_input_cmd(s);
 194                }
 195        }
 196        spin_unlock_irqrestore(&counter->lock, flags);
 197        return retval;
 198}
 199EXPORT_SYMBOL_GPL(ni_tio_cmd);
 200
 201int ni_tio_cmdtest(struct comedi_device *dev,
 202                   struct comedi_subdevice *s,
 203                   struct comedi_cmd *cmd)
 204{
 205        struct ni_gpct *counter = s->private;
 206        int err = 0;
 207        unsigned int sources;
 208
 209        /* Step 1 : check if triggers are trivially valid */
 210
 211        sources = TRIG_NOW | TRIG_INT | TRIG_OTHER;
 212        if (ni_tio_counting_mode_registers_present(counter->counter_dev))
 213                sources |= TRIG_EXT;
 214        err |= comedi_check_trigger_src(&cmd->start_src, sources);
 215
 216        err |= comedi_check_trigger_src(&cmd->scan_begin_src,
 217                                        TRIG_FOLLOW | TRIG_EXT | TRIG_OTHER);
 218        err |= comedi_check_trigger_src(&cmd->convert_src,
 219                                        TRIG_NOW | TRIG_EXT | TRIG_OTHER);
 220        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 221        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
 222
 223        if (err)
 224                return 1;
 225
 226        /* Step 2a : make sure trigger sources are unique */
 227
 228        err |= comedi_check_trigger_is_unique(cmd->start_src);
 229        err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
 230        err |= comedi_check_trigger_is_unique(cmd->convert_src);
 231
 232        /* Step 2b : and mutually compatible */
 233
 234        if (cmd->convert_src != TRIG_NOW && cmd->scan_begin_src != TRIG_FOLLOW)
 235                err |= -EINVAL;
 236
 237        if (err)
 238                return 2;
 239
 240        /* Step 3: check if arguments are trivially valid */
 241
 242        switch (cmd->start_src) {
 243        case TRIG_NOW:
 244        case TRIG_INT:
 245        case TRIG_OTHER:
 246                err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 247                break;
 248        case TRIG_EXT:
 249                /* start_arg is the start_trigger passed to ni_tio_arm() */
 250                break;
 251        }
 252
 253        if (cmd->scan_begin_src != TRIG_EXT)
 254                err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 255
 256        if (cmd->convert_src != TRIG_EXT)
 257                err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 258
 259        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 260                                           cmd->chanlist_len);
 261        err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 262
 263        if (err)
 264                return 3;
 265
 266        /* Step 4: fix up any arguments */
 267
 268        /* Step 5: check channel list if it exists */
 269
 270        return 0;
 271}
 272EXPORT_SYMBOL_GPL(ni_tio_cmdtest);
 273
 274int ni_tio_cancel(struct ni_gpct *counter)
 275{
 276        unsigned int cidx = counter->counter_index;
 277        unsigned long flags;
 278
 279        ni_tio_arm(counter, false, 0);
 280        spin_lock_irqsave(&counter->lock, flags);
 281        if (counter->mite_chan)
 282                mite_dma_disarm(counter->mite_chan);
 283        spin_unlock_irqrestore(&counter->lock, flags);
 284        ni_tio_configure_dma(counter, false, false);
 285
 286        ni_tio_set_bits(counter, NITIO_INT_ENA_REG(cidx),
 287                        GI_GATE_INTERRUPT_ENABLE(cidx), 0x0);
 288        return 0;
 289}
 290EXPORT_SYMBOL_GPL(ni_tio_cancel);
 291
 292static int should_ack_gate(struct ni_gpct *counter)
 293{
 294        unsigned long flags;
 295        int retval = 0;
 296
 297        switch (counter->counter_dev->variant) {
 298        case ni_gpct_variant_m_series:
 299        case ni_gpct_variant_660x:
 300                /*
 301                 * not sure if 660x really supports gate interrupts
 302                 * (the bits are not listed in register-level manual)
 303                 */
 304                return 1;
 305        case ni_gpct_variant_e_series:
 306                /*
 307                 * During buffered input counter operation for e-series,
 308                 * the gate interrupt is acked automatically by the dma
 309                 * controller, due to the Gi_Read/Write_Acknowledges_IRQ
 310                 * bits in the input select register.
 311                 */
 312                spin_lock_irqsave(&counter->lock, flags);
 313                {
 314                        if (!counter->mite_chan ||
 315                            counter->mite_chan->dir != COMEDI_INPUT ||
 316                            (mite_done(counter->mite_chan))) {
 317                                retval = 1;
 318                        }
 319                }
 320                spin_unlock_irqrestore(&counter->lock, flags);
 321                break;
 322        }
 323        return retval;
 324}
 325
 326static void ni_tio_acknowledge_and_confirm(struct ni_gpct *counter,
 327                                           int *gate_error,
 328                                           int *tc_error,
 329                                           int *perm_stale_data)
 330{
 331        unsigned int cidx = counter->counter_index;
 332        const unsigned short gxx_status = ni_tio_read(counter,
 333                                                NITIO_SHARED_STATUS_REG(cidx));
 334        const unsigned short gi_status = ni_tio_read(counter,
 335                                                NITIO_STATUS_REG(cidx));
 336        unsigned int ack = 0;
 337
 338        if (gate_error)
 339                *gate_error = 0;
 340        if (tc_error)
 341                *tc_error = 0;
 342        if (perm_stale_data)
 343                *perm_stale_data = 0;
 344
 345        if (gxx_status & GI_GATE_ERROR(cidx)) {
 346                ack |= GI_GATE_ERROR_CONFIRM(cidx);
 347                if (gate_error) {
 348                        /*
 349                         * 660x don't support automatic acknowledgment
 350                         * of gate interrupt via dma read/write
 351                         * and report bogus gate errors
 352                         */
 353                        if (counter->counter_dev->variant !=
 354                            ni_gpct_variant_660x)
 355                                *gate_error = 1;
 356                }
 357        }
 358        if (gxx_status & GI_TC_ERROR(cidx)) {
 359                ack |= GI_TC_ERROR_CONFIRM(cidx);
 360                if (tc_error)
 361                        *tc_error = 1;
 362        }
 363        if (gi_status & GI_TC)
 364                ack |= GI_TC_INTERRUPT_ACK;
 365        if (gi_status & GI_GATE_INTERRUPT) {
 366                if (should_ack_gate(counter))
 367                        ack |= GI_GATE_INTERRUPT_ACK;
 368        }
 369        if (ack)
 370                ni_tio_write(counter, ack, NITIO_INT_ACK_REG(cidx));
 371        if (ni_tio_get_soft_copy(counter, NITIO_MODE_REG(cidx)) &
 372            GI_LOADING_ON_GATE) {
 373                if (ni_tio_read(counter, NITIO_STATUS2_REG(cidx)) &
 374                    GI_PERMANENT_STALE(cidx)) {
 375                        dev_info(counter->counter_dev->dev->class_dev,
 376                                 "%s: Gi_Permanent_Stale_Data detected.\n",
 377                                 __func__);
 378                        if (perm_stale_data)
 379                                *perm_stale_data = 1;
 380                }
 381        }
 382}
 383
 384void ni_tio_acknowledge(struct ni_gpct *counter)
 385{
 386        ni_tio_acknowledge_and_confirm(counter, NULL, NULL, NULL);
 387}
 388EXPORT_SYMBOL_GPL(ni_tio_acknowledge);
 389
 390void ni_tio_handle_interrupt(struct ni_gpct *counter,
 391                             struct comedi_subdevice *s)
 392{
 393        unsigned int cidx = counter->counter_index;
 394        unsigned long flags;
 395        int gate_error;
 396        int tc_error;
 397        int perm_stale_data;
 398
 399        ni_tio_acknowledge_and_confirm(counter, &gate_error, &tc_error,
 400                                       &perm_stale_data);
 401        if (gate_error) {
 402                dev_notice(counter->counter_dev->dev->class_dev,
 403                           "%s: Gi_Gate_Error detected.\n", __func__);
 404                s->async->events |= COMEDI_CB_OVERFLOW;
 405        }
 406        if (perm_stale_data)
 407                s->async->events |= COMEDI_CB_ERROR;
 408        switch (counter->counter_dev->variant) {
 409        case ni_gpct_variant_m_series:
 410        case ni_gpct_variant_660x:
 411                if (ni_tio_read(counter, NITIO_DMA_STATUS_REG(cidx)) &
 412                    GI_DRQ_ERROR) {
 413                        dev_notice(counter->counter_dev->dev->class_dev,
 414                                   "%s: Gi_DRQ_Error detected.\n", __func__);
 415                        s->async->events |= COMEDI_CB_OVERFLOW;
 416                }
 417                break;
 418        case ni_gpct_variant_e_series:
 419                break;
 420        }
 421        spin_lock_irqsave(&counter->lock, flags);
 422        if (counter->mite_chan)
 423                mite_ack_linkc(counter->mite_chan, s, true);
 424        spin_unlock_irqrestore(&counter->lock, flags);
 425}
 426EXPORT_SYMBOL_GPL(ni_tio_handle_interrupt);
 427
 428void ni_tio_set_mite_channel(struct ni_gpct *counter,
 429                             struct mite_channel *mite_chan)
 430{
 431        unsigned long flags;
 432
 433        spin_lock_irqsave(&counter->lock, flags);
 434        counter->mite_chan = mite_chan;
 435        spin_unlock_irqrestore(&counter->lock, flags);
 436}
 437EXPORT_SYMBOL_GPL(ni_tio_set_mite_channel);
 438
 439static int __init ni_tiocmd_init_module(void)
 440{
 441        return 0;
 442}
 443module_init(ni_tiocmd_init_module);
 444
 445static void __exit ni_tiocmd_cleanup_module(void)
 446{
 447}
 448module_exit(ni_tiocmd_cleanup_module);
 449
 450MODULE_AUTHOR("Comedi <comedi@comedi.org>");
 451MODULE_DESCRIPTION("Comedi command support for NI general-purpose counters");
 452MODULE_LICENSE("GPL");
 453