linux/drivers/comedi/drivers/amplc_dio200_common.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * comedi/drivers/amplc_dio200_common.c
   4 *
   5 * Common support code for "amplc_dio200" and "amplc_dio200_pci".
   6 *
   7 * Copyright (C) 2005-2013 MEV Ltd. <https://www.mev.co.uk/>
   8 *
   9 * COMEDI - Linux Control and Measurement Device Interface
  10 * Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/interrupt.h>
  15
  16#include "../comedidev.h"
  17
  18#include "amplc_dio200.h"
  19#include "comedi_8254.h"
  20#include "8255.h"               /* only for register defines */
  21
  22/* 200 series registers */
  23#define DIO200_IO_SIZE          0x20
  24#define DIO200_PCIE_IO_SIZE     0x4000
  25#define DIO200_CLK_SCE(x)       (0x18 + (x))    /* Group X/Y/Z clock sel reg */
  26#define DIO200_GAT_SCE(x)       (0x1b + (x))    /* Group X/Y/Z gate sel reg */
  27#define DIO200_INT_SCE          0x1e    /* Interrupt enable/status register */
  28/* Extra registers for new PCIe boards */
  29#define DIO200_ENHANCE          0x20    /* 1 to enable enhanced features */
  30#define DIO200_VERSION          0x24    /* Hardware version register */
  31#define DIO200_TS_CONFIG        0x600   /* Timestamp timer config register */
  32#define DIO200_TS_COUNT         0x602   /* Timestamp timer count register */
  33
  34/*
  35 * Functions for constructing value for DIO_200_?CLK_SCE and
  36 * DIO_200_?GAT_SCE registers:
  37 *
  38 * 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2.
  39 * 'chan' is the channel: 0, 1 or 2.
  40 * 'source' is the signal source: 0 to 7, or 0 to 31 for "enhanced" boards.
  41 */
  42static unsigned char clk_gat_sce(unsigned int which, unsigned int chan,
  43                                 unsigned int source)
  44{
  45        return (which << 5) | (chan << 3) |
  46               ((source & 030) << 3) | (source & 007);
  47}
  48
  49/*
  50 * Periods of the internal clock sources in nanoseconds.
  51 */
  52static const unsigned int clock_period[32] = {
  53        [1] = 100,              /* 10 MHz */
  54        [2] = 1000,             /* 1 MHz */
  55        [3] = 10000,            /* 100 kHz */
  56        [4] = 100000,           /* 10 kHz */
  57        [5] = 1000000,          /* 1 kHz */
  58        [11] = 50,              /* 20 MHz (enhanced boards) */
  59        /* clock sources 12 and later reserved for enhanced boards */
  60};
  61
  62/*
  63 * Timestamp timer configuration register (for new PCIe boards).
  64 */
  65#define TS_CONFIG_RESET         0x100   /* Reset counter to zero. */
  66#define TS_CONFIG_CLK_SRC_MASK  0x0FF   /* Clock source. */
  67#define TS_CONFIG_MAX_CLK_SRC   2       /* Maximum clock source value. */
  68
  69/*
  70 * Periods of the timestamp timer clock sources in nanoseconds.
  71 */
  72static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = {
  73        1,                      /* 1 nanosecond (but with 20 ns granularity). */
  74        1000,                   /* 1 microsecond. */
  75        1000000,                /* 1 millisecond. */
  76};
  77
  78struct dio200_subdev_8255 {
  79        unsigned int ofs;               /* DIO base offset */
  80};
  81
  82struct dio200_subdev_intr {
  83        spinlock_t spinlock;    /* protects the 'active' flag */
  84        unsigned int ofs;
  85        unsigned int valid_isns;
  86        unsigned int enabled_isns;
  87        unsigned int active:1;
  88};
  89
  90static unsigned char dio200_read8(struct comedi_device *dev,
  91                                  unsigned int offset)
  92{
  93        const struct dio200_board *board = dev->board_ptr;
  94
  95        if (board->is_pcie)
  96                offset <<= 3;
  97
  98        if (dev->mmio)
  99                return readb(dev->mmio + offset);
 100        return inb(dev->iobase + offset);
 101}
 102
 103static void dio200_write8(struct comedi_device *dev,
 104                          unsigned int offset, unsigned char val)
 105{
 106        const struct dio200_board *board = dev->board_ptr;
 107
 108        if (board->is_pcie)
 109                offset <<= 3;
 110
 111        if (dev->mmio)
 112                writeb(val, dev->mmio + offset);
 113        else
 114                outb(val, dev->iobase + offset);
 115}
 116
 117static unsigned int dio200_read32(struct comedi_device *dev,
 118                                  unsigned int offset)
 119{
 120        const struct dio200_board *board = dev->board_ptr;
 121
 122        if (board->is_pcie)
 123                offset <<= 3;
 124
 125        if (dev->mmio)
 126                return readl(dev->mmio + offset);
 127        return inl(dev->iobase + offset);
 128}
 129
 130static void dio200_write32(struct comedi_device *dev,
 131                           unsigned int offset, unsigned int val)
 132{
 133        const struct dio200_board *board = dev->board_ptr;
 134
 135        if (board->is_pcie)
 136                offset <<= 3;
 137
 138        if (dev->mmio)
 139                writel(val, dev->mmio + offset);
 140        else
 141                outl(val, dev->iobase + offset);
 142}
 143
 144static unsigned int dio200_subdev_8254_offset(struct comedi_device *dev,
 145                                              struct comedi_subdevice *s)
 146{
 147        const struct dio200_board *board = dev->board_ptr;
 148        struct comedi_8254 *i8254 = s->private;
 149        unsigned int offset;
 150
 151        /* get the offset that was passed to comedi_8254_*_init() */
 152        if (dev->mmio)
 153                offset = i8254->mmio - dev->mmio;
 154        else
 155                offset = i8254->iobase - dev->iobase;
 156
 157        /* remove the shift that was added for PCIe boards */
 158        if (board->is_pcie)
 159                offset >>= 3;
 160
 161        /* this offset now works for the dio200_{read,write} helpers */
 162        return offset;
 163}
 164
 165static int dio200_subdev_intr_insn_bits(struct comedi_device *dev,
 166                                        struct comedi_subdevice *s,
 167                                        struct comedi_insn *insn,
 168                                        unsigned int *data)
 169{
 170        const struct dio200_board *board = dev->board_ptr;
 171        struct dio200_subdev_intr *subpriv = s->private;
 172
 173        if (board->has_int_sce) {
 174                /* Just read the interrupt status register.  */
 175                data[1] = dio200_read8(dev, subpriv->ofs) & subpriv->valid_isns;
 176        } else {
 177                /* No interrupt status register. */
 178                data[0] = 0;
 179        }
 180
 181        return insn->n;
 182}
 183
 184static void dio200_stop_intr(struct comedi_device *dev,
 185                             struct comedi_subdevice *s)
 186{
 187        const struct dio200_board *board = dev->board_ptr;
 188        struct dio200_subdev_intr *subpriv = s->private;
 189
 190        subpriv->active = false;
 191        subpriv->enabled_isns = 0;
 192        if (board->has_int_sce)
 193                dio200_write8(dev, subpriv->ofs, 0);
 194}
 195
 196static void dio200_start_intr(struct comedi_device *dev,
 197                              struct comedi_subdevice *s)
 198{
 199        const struct dio200_board *board = dev->board_ptr;
 200        struct dio200_subdev_intr *subpriv = s->private;
 201        struct comedi_cmd *cmd = &s->async->cmd;
 202        unsigned int n;
 203        unsigned int isn_bits;
 204
 205        /* Determine interrupt sources to enable. */
 206        isn_bits = 0;
 207        if (cmd->chanlist) {
 208                for (n = 0; n < cmd->chanlist_len; n++)
 209                        isn_bits |= (1U << CR_CHAN(cmd->chanlist[n]));
 210        }
 211        isn_bits &= subpriv->valid_isns;
 212        /* Enable interrupt sources. */
 213        subpriv->enabled_isns = isn_bits;
 214        if (board->has_int_sce)
 215                dio200_write8(dev, subpriv->ofs, isn_bits);
 216}
 217
 218static int dio200_inttrig_start_intr(struct comedi_device *dev,
 219                                     struct comedi_subdevice *s,
 220                                     unsigned int trig_num)
 221{
 222        struct dio200_subdev_intr *subpriv = s->private;
 223        struct comedi_cmd *cmd = &s->async->cmd;
 224        unsigned long flags;
 225
 226        if (trig_num != cmd->start_arg)
 227                return -EINVAL;
 228
 229        spin_lock_irqsave(&subpriv->spinlock, flags);
 230        s->async->inttrig = NULL;
 231        if (subpriv->active)
 232                dio200_start_intr(dev, s);
 233
 234        spin_unlock_irqrestore(&subpriv->spinlock, flags);
 235
 236        return 1;
 237}
 238
 239static void dio200_read_scan_intr(struct comedi_device *dev,
 240                                  struct comedi_subdevice *s,
 241                                  unsigned int triggered)
 242{
 243        struct comedi_cmd *cmd = &s->async->cmd;
 244        unsigned short val;
 245        unsigned int n, ch;
 246
 247        val = 0;
 248        for (n = 0; n < cmd->chanlist_len; n++) {
 249                ch = CR_CHAN(cmd->chanlist[n]);
 250                if (triggered & (1U << ch))
 251                        val |= (1U << n);
 252        }
 253
 254        comedi_buf_write_samples(s, &val, 1);
 255
 256        if (cmd->stop_src == TRIG_COUNT &&
 257            s->async->scans_done >= cmd->stop_arg)
 258                s->async->events |= COMEDI_CB_EOA;
 259}
 260
 261static int dio200_handle_read_intr(struct comedi_device *dev,
 262                                   struct comedi_subdevice *s)
 263{
 264        const struct dio200_board *board = dev->board_ptr;
 265        struct dio200_subdev_intr *subpriv = s->private;
 266        unsigned int triggered;
 267        unsigned int intstat;
 268        unsigned int cur_enabled;
 269        unsigned long flags;
 270
 271        triggered = 0;
 272
 273        spin_lock_irqsave(&subpriv->spinlock, flags);
 274        if (board->has_int_sce) {
 275                /*
 276                 * Collect interrupt sources that have triggered and disable
 277                 * them temporarily.  Loop around until no extra interrupt
 278                 * sources have triggered, at which point, the valid part of
 279                 * the interrupt status register will read zero, clearing the
 280                 * cause of the interrupt.
 281                 *
 282                 * Mask off interrupt sources already seen to avoid infinite
 283                 * loop in case of misconfiguration.
 284                 */
 285                cur_enabled = subpriv->enabled_isns;
 286                while ((intstat = (dio200_read8(dev, subpriv->ofs) &
 287                                   subpriv->valid_isns & ~triggered)) != 0) {
 288                        triggered |= intstat;
 289                        cur_enabled &= ~triggered;
 290                        dio200_write8(dev, subpriv->ofs, cur_enabled);
 291                }
 292        } else {
 293                /*
 294                 * No interrupt status register.  Assume the single interrupt
 295                 * source has triggered.
 296                 */
 297                triggered = subpriv->enabled_isns;
 298        }
 299
 300        if (triggered) {
 301                /*
 302                 * Some interrupt sources have triggered and have been
 303                 * temporarily disabled to clear the cause of the interrupt.
 304                 *
 305                 * Reenable them NOW to minimize the time they are disabled.
 306                 */
 307                cur_enabled = subpriv->enabled_isns;
 308                if (board->has_int_sce)
 309                        dio200_write8(dev, subpriv->ofs, cur_enabled);
 310
 311                if (subpriv->active) {
 312                        /*
 313                         * The command is still active.
 314                         *
 315                         * Ignore interrupt sources that the command isn't
 316                         * interested in (just in case there's a race
 317                         * condition).
 318                         */
 319                        if (triggered & subpriv->enabled_isns) {
 320                                /* Collect scan data. */
 321                                dio200_read_scan_intr(dev, s, triggered);
 322                        }
 323                }
 324        }
 325        spin_unlock_irqrestore(&subpriv->spinlock, flags);
 326
 327        comedi_handle_events(dev, s);
 328
 329        return (triggered != 0);
 330}
 331
 332static int dio200_subdev_intr_cancel(struct comedi_device *dev,
 333                                     struct comedi_subdevice *s)
 334{
 335        struct dio200_subdev_intr *subpriv = s->private;
 336        unsigned long flags;
 337
 338        spin_lock_irqsave(&subpriv->spinlock, flags);
 339        if (subpriv->active)
 340                dio200_stop_intr(dev, s);
 341
 342        spin_unlock_irqrestore(&subpriv->spinlock, flags);
 343
 344        return 0;
 345}
 346
 347static int dio200_subdev_intr_cmdtest(struct comedi_device *dev,
 348                                      struct comedi_subdevice *s,
 349                                      struct comedi_cmd *cmd)
 350{
 351        int err = 0;
 352
 353        /* Step 1 : check if triggers are trivially valid */
 354
 355        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
 356        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
 357        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
 358        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 359        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 360
 361        if (err)
 362                return 1;
 363
 364        /* Step 2a : make sure trigger sources are unique */
 365
 366        err |= comedi_check_trigger_is_unique(cmd->start_src);
 367        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 368
 369        /* Step 2b : and mutually compatible */
 370
 371        if (err)
 372                return 2;
 373
 374        /* Step 3: check if arguments are trivially valid */
 375
 376        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 377        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 378        err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 379        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 380                                           cmd->chanlist_len);
 381
 382        if (cmd->stop_src == TRIG_COUNT)
 383                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 384        else    /* TRIG_NONE */
 385                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 386
 387        if (err)
 388                return 3;
 389
 390        /* step 4: fix up any arguments */
 391
 392        /* if (err) return 4; */
 393
 394        return 0;
 395}
 396
 397static int dio200_subdev_intr_cmd(struct comedi_device *dev,
 398                                  struct comedi_subdevice *s)
 399{
 400        struct comedi_cmd *cmd = &s->async->cmd;
 401        struct dio200_subdev_intr *subpriv = s->private;
 402        unsigned long flags;
 403
 404        spin_lock_irqsave(&subpriv->spinlock, flags);
 405
 406        subpriv->active = true;
 407
 408        if (cmd->start_src == TRIG_INT)
 409                s->async->inttrig = dio200_inttrig_start_intr;
 410        else    /* TRIG_NOW */
 411                dio200_start_intr(dev, s);
 412
 413        spin_unlock_irqrestore(&subpriv->spinlock, flags);
 414
 415        return 0;
 416}
 417
 418static int dio200_subdev_intr_init(struct comedi_device *dev,
 419                                   struct comedi_subdevice *s,
 420                                   unsigned int offset,
 421                                   unsigned int valid_isns)
 422{
 423        const struct dio200_board *board = dev->board_ptr;
 424        struct dio200_subdev_intr *subpriv;
 425
 426        subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
 427        if (!subpriv)
 428                return -ENOMEM;
 429
 430        subpriv->ofs = offset;
 431        subpriv->valid_isns = valid_isns;
 432        spin_lock_init(&subpriv->spinlock);
 433
 434        if (board->has_int_sce)
 435                /* Disable interrupt sources. */
 436                dio200_write8(dev, subpriv->ofs, 0);
 437
 438        s->type = COMEDI_SUBD_DI;
 439        s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_PACKED;
 440        if (board->has_int_sce) {
 441                s->n_chan = DIO200_MAX_ISNS;
 442                s->len_chanlist = DIO200_MAX_ISNS;
 443        } else {
 444                /* No interrupt source register.  Support single channel. */
 445                s->n_chan = 1;
 446                s->len_chanlist = 1;
 447        }
 448        s->range_table = &range_digital;
 449        s->maxdata = 1;
 450        s->insn_bits = dio200_subdev_intr_insn_bits;
 451        s->do_cmdtest = dio200_subdev_intr_cmdtest;
 452        s->do_cmd = dio200_subdev_intr_cmd;
 453        s->cancel = dio200_subdev_intr_cancel;
 454
 455        return 0;
 456}
 457
 458static irqreturn_t dio200_interrupt(int irq, void *d)
 459{
 460        struct comedi_device *dev = d;
 461        struct comedi_subdevice *s = dev->read_subdev;
 462        int handled;
 463
 464        if (!dev->attached)
 465                return IRQ_NONE;
 466
 467        handled = dio200_handle_read_intr(dev, s);
 468
 469        return IRQ_RETVAL(handled);
 470}
 471
 472static void dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
 473                                            struct comedi_subdevice *s,
 474                                            unsigned int chan,
 475                                            unsigned int src)
 476{
 477        unsigned int offset = dio200_subdev_8254_offset(dev, s);
 478
 479        dio200_write8(dev, DIO200_GAT_SCE(offset >> 3),
 480                      clk_gat_sce((offset >> 2) & 1, chan, src));
 481}
 482
 483static void dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
 484                                             struct comedi_subdevice *s,
 485                                             unsigned int chan,
 486                                             unsigned int src)
 487{
 488        unsigned int offset = dio200_subdev_8254_offset(dev, s);
 489
 490        dio200_write8(dev, DIO200_CLK_SCE(offset >> 3),
 491                      clk_gat_sce((offset >> 2) & 1, chan, src));
 492}
 493
 494static int dio200_subdev_8254_config(struct comedi_device *dev,
 495                                     struct comedi_subdevice *s,
 496                                     struct comedi_insn *insn,
 497                                     unsigned int *data)
 498{
 499        const struct dio200_board *board = dev->board_ptr;
 500        struct comedi_8254 *i8254 = s->private;
 501        unsigned int chan = CR_CHAN(insn->chanspec);
 502        unsigned int max_src = board->is_pcie ? 31 : 7;
 503        unsigned int src;
 504
 505        if (!board->has_clk_gat_sce)
 506                return -EINVAL;
 507
 508        switch (data[0]) {
 509        case INSN_CONFIG_SET_GATE_SRC:
 510                src = data[2];
 511                if (src > max_src)
 512                        return -EINVAL;
 513
 514                dio200_subdev_8254_set_gate_src(dev, s, chan, src);
 515                i8254->gate_src[chan] = src;
 516                break;
 517        case INSN_CONFIG_GET_GATE_SRC:
 518                data[2] = i8254->gate_src[chan];
 519                break;
 520        case INSN_CONFIG_SET_CLOCK_SRC:
 521                src = data[1];
 522                if (src > max_src)
 523                        return -EINVAL;
 524
 525                dio200_subdev_8254_set_clock_src(dev, s, chan, src);
 526                i8254->clock_src[chan] = src;
 527                break;
 528        case INSN_CONFIG_GET_CLOCK_SRC:
 529                data[1] = i8254->clock_src[chan];
 530                data[2] = clock_period[i8254->clock_src[chan]];
 531                break;
 532        default:
 533                return -EINVAL;
 534        }
 535
 536        return insn->n;
 537}
 538
 539static int dio200_subdev_8254_init(struct comedi_device *dev,
 540                                   struct comedi_subdevice *s,
 541                                   unsigned int offset)
 542{
 543        const struct dio200_board *board = dev->board_ptr;
 544        struct comedi_8254 *i8254;
 545        unsigned int regshift;
 546        int chan;
 547
 548        /*
 549         * PCIe boards need the offset shifted in order to get the
 550         * correct base address of the timer.
 551         */
 552        if (board->is_pcie) {
 553                offset <<= 3;
 554                regshift = 3;
 555        } else {
 556                regshift = 0;
 557        }
 558
 559        if (dev->mmio) {
 560                i8254 = comedi_8254_mm_init(dev->mmio + offset,
 561                                            0, I8254_IO8, regshift);
 562        } else {
 563                i8254 = comedi_8254_init(dev->iobase + offset,
 564                                         0, I8254_IO8, regshift);
 565        }
 566        if (!i8254)
 567                return -ENOMEM;
 568
 569        comedi_8254_subdevice_init(s, i8254);
 570
 571        i8254->insn_config = dio200_subdev_8254_config;
 572
 573        /*
 574         * There could be multiple timers so this driver does not
 575         * use dev->pacer to save the i8254 pointer. Instead,
 576         * comedi_8254_subdevice_init() saved the i8254 pointer in
 577         * s->private.  Mark the subdevice as having private data
 578         * to be automatically freed when the device is detached.
 579         */
 580        comedi_set_spriv_auto_free(s);
 581
 582        /* Initialize channels. */
 583        if (board->has_clk_gat_sce) {
 584                for (chan = 0; chan < 3; chan++) {
 585                        /* Gate source 0 is VCC (logic 1). */
 586                        dio200_subdev_8254_set_gate_src(dev, s, chan, 0);
 587                        /* Clock source 0 is the dedicated clock input. */
 588                        dio200_subdev_8254_set_clock_src(dev, s, chan, 0);
 589                }
 590        }
 591
 592        return 0;
 593}
 594
 595static void dio200_subdev_8255_set_dir(struct comedi_device *dev,
 596                                       struct comedi_subdevice *s)
 597{
 598        struct dio200_subdev_8255 *subpriv = s->private;
 599        int config;
 600
 601        config = I8255_CTRL_CW;
 602        /* 1 in io_bits indicates output, 1 in config indicates input */
 603        if (!(s->io_bits & 0x0000ff))
 604                config |= I8255_CTRL_A_IO;
 605        if (!(s->io_bits & 0x00ff00))
 606                config |= I8255_CTRL_B_IO;
 607        if (!(s->io_bits & 0x0f0000))
 608                config |= I8255_CTRL_C_LO_IO;
 609        if (!(s->io_bits & 0xf00000))
 610                config |= I8255_CTRL_C_HI_IO;
 611        dio200_write8(dev, subpriv->ofs + I8255_CTRL_REG, config);
 612}
 613
 614static int dio200_subdev_8255_bits(struct comedi_device *dev,
 615                                   struct comedi_subdevice *s,
 616                                   struct comedi_insn *insn,
 617                                   unsigned int *data)
 618{
 619        struct dio200_subdev_8255 *subpriv = s->private;
 620        unsigned int mask;
 621        unsigned int val;
 622
 623        mask = comedi_dio_update_state(s, data);
 624        if (mask) {
 625                if (mask & 0xff) {
 626                        dio200_write8(dev, subpriv->ofs + I8255_DATA_A_REG,
 627                                      s->state & 0xff);
 628                }
 629                if (mask & 0xff00) {
 630                        dio200_write8(dev, subpriv->ofs + I8255_DATA_B_REG,
 631                                      (s->state >> 8) & 0xff);
 632                }
 633                if (mask & 0xff0000) {
 634                        dio200_write8(dev, subpriv->ofs + I8255_DATA_C_REG,
 635                                      (s->state >> 16) & 0xff);
 636                }
 637        }
 638
 639        val = dio200_read8(dev, subpriv->ofs + I8255_DATA_A_REG);
 640        val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_B_REG) << 8;
 641        val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_C_REG) << 16;
 642
 643        data[1] = val;
 644
 645        return insn->n;
 646}
 647
 648static int dio200_subdev_8255_config(struct comedi_device *dev,
 649                                     struct comedi_subdevice *s,
 650                                     struct comedi_insn *insn,
 651                                     unsigned int *data)
 652{
 653        unsigned int chan = CR_CHAN(insn->chanspec);
 654        unsigned int mask;
 655        int ret;
 656
 657        if (chan < 8)
 658                mask = 0x0000ff;
 659        else if (chan < 16)
 660                mask = 0x00ff00;
 661        else if (chan < 20)
 662                mask = 0x0f0000;
 663        else
 664                mask = 0xf00000;
 665
 666        ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 667        if (ret)
 668                return ret;
 669
 670        dio200_subdev_8255_set_dir(dev, s);
 671
 672        return insn->n;
 673}
 674
 675static int dio200_subdev_8255_init(struct comedi_device *dev,
 676                                   struct comedi_subdevice *s,
 677                                   unsigned int offset)
 678{
 679        struct dio200_subdev_8255 *subpriv;
 680
 681        subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
 682        if (!subpriv)
 683                return -ENOMEM;
 684
 685        subpriv->ofs = offset;
 686
 687        s->type = COMEDI_SUBD_DIO;
 688        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 689        s->n_chan = 24;
 690        s->range_table = &range_digital;
 691        s->maxdata = 1;
 692        s->insn_bits = dio200_subdev_8255_bits;
 693        s->insn_config = dio200_subdev_8255_config;
 694        dio200_subdev_8255_set_dir(dev, s);
 695        return 0;
 696}
 697
 698static int dio200_subdev_timer_read(struct comedi_device *dev,
 699                                    struct comedi_subdevice *s,
 700                                    struct comedi_insn *insn,
 701                                    unsigned int *data)
 702{
 703        unsigned int n;
 704
 705        for (n = 0; n < insn->n; n++)
 706                data[n] = dio200_read32(dev, DIO200_TS_COUNT);
 707        return n;
 708}
 709
 710static void dio200_subdev_timer_reset(struct comedi_device *dev,
 711                                      struct comedi_subdevice *s)
 712{
 713        unsigned int clock;
 714
 715        clock = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
 716        dio200_write32(dev, DIO200_TS_CONFIG, clock | TS_CONFIG_RESET);
 717        dio200_write32(dev, DIO200_TS_CONFIG, clock);
 718}
 719
 720static void dio200_subdev_timer_get_clock_src(struct comedi_device *dev,
 721                                              struct comedi_subdevice *s,
 722                                              unsigned int *src,
 723                                              unsigned int *period)
 724{
 725        unsigned int clk;
 726
 727        clk = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
 728        *src = clk;
 729        *period = (clk < ARRAY_SIZE(ts_clock_period)) ?
 730                  ts_clock_period[clk] : 0;
 731}
 732
 733static int dio200_subdev_timer_set_clock_src(struct comedi_device *dev,
 734                                             struct comedi_subdevice *s,
 735                                             unsigned int src)
 736{
 737        if (src > TS_CONFIG_MAX_CLK_SRC)
 738                return -EINVAL;
 739        dio200_write32(dev, DIO200_TS_CONFIG, src);
 740        return 0;
 741}
 742
 743static int dio200_subdev_timer_config(struct comedi_device *dev,
 744                                      struct comedi_subdevice *s,
 745                                      struct comedi_insn *insn,
 746                                      unsigned int *data)
 747{
 748        int ret = 0;
 749
 750        switch (data[0]) {
 751        case INSN_CONFIG_RESET:
 752                dio200_subdev_timer_reset(dev, s);
 753                break;
 754        case INSN_CONFIG_SET_CLOCK_SRC:
 755                ret = dio200_subdev_timer_set_clock_src(dev, s, data[1]);
 756                if (ret < 0)
 757                        ret = -EINVAL;
 758                break;
 759        case INSN_CONFIG_GET_CLOCK_SRC:
 760                dio200_subdev_timer_get_clock_src(dev, s, &data[1], &data[2]);
 761                break;
 762        default:
 763                ret = -EINVAL;
 764                break;
 765        }
 766        return ret < 0 ? ret : insn->n;
 767}
 768
 769void amplc_dio200_set_enhance(struct comedi_device *dev, unsigned char val)
 770{
 771        dio200_write8(dev, DIO200_ENHANCE, val);
 772}
 773EXPORT_SYMBOL_GPL(amplc_dio200_set_enhance);
 774
 775int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq,
 776                               unsigned long req_irq_flags)
 777{
 778        const struct dio200_board *board = dev->board_ptr;
 779        struct comedi_subdevice *s;
 780        unsigned int n;
 781        int ret;
 782
 783        ret = comedi_alloc_subdevices(dev, board->n_subdevs);
 784        if (ret)
 785                return ret;
 786
 787        for (n = 0; n < dev->n_subdevices; n++) {
 788                s = &dev->subdevices[n];
 789                switch (board->sdtype[n]) {
 790                case sd_8254:
 791                        /* counter subdevice (8254) */
 792                        ret = dio200_subdev_8254_init(dev, s,
 793                                                      board->sdinfo[n]);
 794                        if (ret < 0)
 795                                return ret;
 796                        break;
 797                case sd_8255:
 798                        /* digital i/o subdevice (8255) */
 799                        ret = dio200_subdev_8255_init(dev, s,
 800                                                      board->sdinfo[n]);
 801                        if (ret < 0)
 802                                return ret;
 803                        break;
 804                case sd_intr:
 805                        /* 'INTERRUPT' subdevice */
 806                        if (irq && !dev->read_subdev) {
 807                                ret = dio200_subdev_intr_init(dev, s,
 808                                                              DIO200_INT_SCE,
 809                                                              board->sdinfo[n]);
 810                                if (ret < 0)
 811                                        return ret;
 812                                dev->read_subdev = s;
 813                        } else {
 814                                s->type = COMEDI_SUBD_UNUSED;
 815                        }
 816                        break;
 817                case sd_timer:
 818                        s->type         = COMEDI_SUBD_TIMER;
 819                        s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
 820                        s->n_chan       = 1;
 821                        s->maxdata      = 0xffffffff;
 822                        s->insn_read    = dio200_subdev_timer_read;
 823                        s->insn_config  = dio200_subdev_timer_config;
 824                        break;
 825                default:
 826                        s->type = COMEDI_SUBD_UNUSED;
 827                        break;
 828                }
 829        }
 830
 831        if (irq && dev->read_subdev) {
 832                if (request_irq(irq, dio200_interrupt, req_irq_flags,
 833                                dev->board_name, dev) >= 0) {
 834                        dev->irq = irq;
 835                } else {
 836                        dev_warn(dev->class_dev,
 837                                 "warning! irq %u unavailable!\n", irq);
 838                }
 839        }
 840
 841        return 0;
 842}
 843EXPORT_SYMBOL_GPL(amplc_dio200_common_attach);
 844
 845static int __init amplc_dio200_common_init(void)
 846{
 847        return 0;
 848}
 849module_init(amplc_dio200_common_init);
 850
 851static void __exit amplc_dio200_common_exit(void)
 852{
 853}
 854module_exit(amplc_dio200_common_exit);
 855
 856MODULE_AUTHOR("Comedi https://www.comedi.org");
 857MODULE_DESCRIPTION("Comedi helper for amplc_dio200 and amplc_dio200_pci");
 858MODULE_LICENSE("GPL");
 859