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