linux/drivers/staging/comedi/drivers/dt3000.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/dt3000.c
   3    Data Translation DT3000 series driver
   4
   5    COMEDI - Linux Control and Measurement Device Interface
   6    Copyright (C) 1999 David A. Schleef <ds@schleef.org>
   7
   8    This program is free software; you can redistribute it and/or modify
   9    it under the terms of the GNU General Public License as published by
  10    the Free Software Foundation; either version 2 of the License, or
  11    (at your option) any later version.
  12
  13    This program is distributed in the hope that it will be useful,
  14    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16    GNU General Public License for more details.
  17
  18    You should have received a copy of the GNU General Public License
  19    along with this program; if not, write to the Free Software
  20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21
  22*/
  23/*
  24Driver: dt3000
  25Description: Data Translation DT3000 series
  26Author: ds
  27Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003,
  28  DT3003-PGL, DT3004, DT3005, DT3004-200
  29Updated: Mon, 14 Apr 2008 15:41:24 +0100
  30Status: works
  31
  32Configuration Options: not applicable, uses PCI auto config
  33
  34There is code to support AI commands, but it may not work.
  35
  36AO commands are not supported.
  37*/
  38
  39/*
  40   The DT3000 series is Data Translation's attempt to make a PCI
  41   data acquisition board.  The design of this series is very nice,
  42   since each board has an on-board DSP (Texas Instruments TMS320C52).
  43   However, a few details are a little annoying.  The boards lack
  44   bus-mastering DMA, which eliminates them from serious work.
  45   They also are not capable of autocalibration, which is a common
  46   feature in modern hardware.  The default firmware is pretty bad,
  47   making it nearly impossible to write an RT compatible driver.
  48   It would make an interesting project to write a decent firmware
  49   for these boards.
  50
  51   Data Translation originally wanted an NDA for the documentation
  52   for the 3k series.  However, if you ask nicely, they might send
  53   you the docs without one, also.
  54*/
  55
  56#define DEBUG 1
  57
  58#include <linux/pci.h>
  59#include <linux/delay.h>
  60#include <linux/interrupt.h>
  61
  62#include "../comedidev.h"
  63
  64#include "comedi_fc.h"
  65
  66static const struct comedi_lrange range_dt3000_ai = {
  67        4, {
  68                BIP_RANGE(10),
  69                BIP_RANGE(5),
  70                BIP_RANGE(2.5),
  71                BIP_RANGE(1.25)
  72        }
  73};
  74
  75static const struct comedi_lrange range_dt3000_ai_pgl = {
  76        4, {
  77                BIP_RANGE(10),
  78                BIP_RANGE(1),
  79                BIP_RANGE(0.1),
  80                BIP_RANGE(0.02)
  81        }
  82};
  83
  84enum dt3k_boardid {
  85        BOARD_DT3001,
  86        BOARD_DT3001_PGL,
  87        BOARD_DT3002,
  88        BOARD_DT3003,
  89        BOARD_DT3003_PGL,
  90        BOARD_DT3004,
  91        BOARD_DT3005,
  92};
  93
  94struct dt3k_boardtype {
  95        const char *name;
  96        int adchan;
  97        int adbits;
  98        int ai_speed;
  99        const struct comedi_lrange *adrange;
 100        int dachan;
 101        int dabits;
 102};
 103
 104static const struct dt3k_boardtype dt3k_boardtypes[] = {
 105        [BOARD_DT3001] = {
 106                .name           = "dt3001",
 107                .adchan         = 16,
 108                .adbits         = 12,
 109                .adrange        = &range_dt3000_ai,
 110                .ai_speed       = 3000,
 111                .dachan         = 2,
 112                .dabits         = 12,
 113        },
 114        [BOARD_DT3001_PGL] = {
 115                .name           = "dt3001-pgl",
 116                .adchan         = 16,
 117                .adbits         = 12,
 118                .adrange        = &range_dt3000_ai_pgl,
 119                .ai_speed       = 3000,
 120                .dachan         = 2,
 121                .dabits         = 12,
 122        },
 123        [BOARD_DT3002] = {
 124                .name           = "dt3002",
 125                .adchan         = 32,
 126                .adbits         = 12,
 127                .adrange        = &range_dt3000_ai,
 128                .ai_speed       = 3000,
 129        },
 130        [BOARD_DT3003] = {
 131                .name           = "dt3003",
 132                .adchan         = 64,
 133                .adbits         = 12,
 134                .adrange        = &range_dt3000_ai,
 135                .ai_speed       = 3000,
 136                .dachan         = 2,
 137                .dabits         = 12,
 138        },
 139        [BOARD_DT3003_PGL] = {
 140                .name           = "dt3003-pgl",
 141                .adchan         = 64,
 142                .adbits         = 12,
 143                .adrange        = &range_dt3000_ai_pgl,
 144                .ai_speed       = 3000,
 145                .dachan         = 2,
 146                .dabits         = 12,
 147        },
 148        [BOARD_DT3004] = {
 149                .name           = "dt3004",
 150                .adchan         = 16,
 151                .adbits         = 16,
 152                .adrange        = &range_dt3000_ai,
 153                .ai_speed       = 10000,
 154                .dachan         = 2,
 155                .dabits         = 12,
 156        },
 157        [BOARD_DT3005] = {
 158                .name           = "dt3005",     /* a.k.a. 3004-200 */
 159                .adchan         = 16,
 160                .adbits         = 16,
 161                .adrange        = &range_dt3000_ai,
 162                .ai_speed       = 5000,
 163                .dachan         = 2,
 164                .dabits         = 12,
 165        },
 166};
 167
 168/* dual-ported RAM location definitions */
 169
 170#define DPR_DAC_buffer          (4*0x000)
 171#define DPR_ADC_buffer          (4*0x800)
 172#define DPR_Command             (4*0xfd3)
 173#define DPR_SubSys              (4*0xfd3)
 174#define DPR_Encode              (4*0xfd4)
 175#define DPR_Params(a)           (4*(0xfd5+(a)))
 176#define DPR_Tick_Reg_Lo         (4*0xff5)
 177#define DPR_Tick_Reg_Hi         (4*0xff6)
 178#define DPR_DA_Buf_Front        (4*0xff7)
 179#define DPR_DA_Buf_Rear         (4*0xff8)
 180#define DPR_AD_Buf_Front        (4*0xff9)
 181#define DPR_AD_Buf_Rear         (4*0xffa)
 182#define DPR_Int_Mask            (4*0xffb)
 183#define DPR_Intr_Flag           (4*0xffc)
 184#define DPR_Response_Mbx        (4*0xffe)
 185#define DPR_Command_Mbx         (4*0xfff)
 186
 187#define AI_FIFO_DEPTH   2003
 188#define AO_FIFO_DEPTH   2048
 189
 190/* command list */
 191
 192#define CMD_GETBRDINFO          0
 193#define CMD_CONFIG              1
 194#define CMD_GETCONFIG           2
 195#define CMD_START               3
 196#define CMD_STOP                4
 197#define CMD_READSINGLE          5
 198#define CMD_WRITESINGLE         6
 199#define CMD_CALCCLOCK           7
 200#define CMD_READEVENTS          8
 201#define CMD_WRITECTCTRL         16
 202#define CMD_READCTCTRL          17
 203#define CMD_WRITECT             18
 204#define CMD_READCT              19
 205#define CMD_WRITEDATA           32
 206#define CMD_READDATA            33
 207#define CMD_WRITEIO             34
 208#define CMD_READIO              35
 209#define CMD_WRITECODE           36
 210#define CMD_READCODE            37
 211#define CMD_EXECUTE             38
 212#define CMD_HALT                48
 213
 214#define SUBS_AI         0
 215#define SUBS_AO         1
 216#define SUBS_DIN        2
 217#define SUBS_DOUT       3
 218#define SUBS_MEM        4
 219#define SUBS_CT         5
 220
 221/* interrupt flags */
 222#define DT3000_CMDONE           0x80
 223#define DT3000_CTDONE           0x40
 224#define DT3000_DAHWERR          0x20
 225#define DT3000_DASWERR          0x10
 226#define DT3000_DAEMPTY          0x08
 227#define DT3000_ADHWERR          0x04
 228#define DT3000_ADSWERR          0x02
 229#define DT3000_ADFULL           0x01
 230
 231#define DT3000_COMPLETION_MASK  0xff00
 232#define DT3000_COMMAND_MASK     0x00ff
 233#define DT3000_NOTPROCESSED     0x0000
 234#define DT3000_NOERROR          0x5500
 235#define DT3000_ERROR            0xaa00
 236#define DT3000_NOTSUPPORTED     0xff00
 237
 238#define DT3000_EXTERNAL_CLOCK   1
 239#define DT3000_RISING_EDGE      2
 240
 241#define TMODE_MASK              0x1c
 242
 243#define DT3000_AD_TRIG_INTERNAL         (0<<2)
 244#define DT3000_AD_TRIG_EXTERNAL         (1<<2)
 245#define DT3000_AD_RETRIG_INTERNAL       (2<<2)
 246#define DT3000_AD_RETRIG_EXTERNAL       (3<<2)
 247#define DT3000_AD_EXTRETRIG             (4<<2)
 248
 249#define DT3000_CHANNEL_MODE_SE          0
 250#define DT3000_CHANNEL_MODE_DI          1
 251
 252struct dt3k_private {
 253        void __iomem *io_addr;
 254        unsigned int lock;
 255        unsigned int ao_readback[2];
 256        unsigned int ai_front;
 257        unsigned int ai_rear;
 258};
 259
 260#ifdef DEBUG
 261static char *intr_flags[] = {
 262        "AdFull", "AdSwError", "AdHwError", "DaEmpty",
 263        "DaSwError", "DaHwError", "CtDone", "CmDone",
 264};
 265
 266static void debug_intr_flags(unsigned int flags)
 267{
 268        int i;
 269        printk(KERN_DEBUG "dt3k: intr_flags:");
 270        for (i = 0; i < 8; i++) {
 271                if (flags & (1 << i))
 272                        printk(KERN_CONT " %s", intr_flags[i]);
 273        }
 274        printk(KERN_CONT "\n");
 275}
 276#endif
 277
 278#define TIMEOUT 100
 279
 280static void dt3k_send_cmd(struct comedi_device *dev, unsigned int cmd)
 281{
 282        struct dt3k_private *devpriv = dev->private;
 283        int i;
 284        unsigned int status = 0;
 285
 286        writew(cmd, devpriv->io_addr + DPR_Command_Mbx);
 287
 288        for (i = 0; i < TIMEOUT; i++) {
 289                status = readw(devpriv->io_addr + DPR_Command_Mbx);
 290                if ((status & DT3000_COMPLETION_MASK) != DT3000_NOTPROCESSED)
 291                        break;
 292                udelay(1);
 293        }
 294
 295        if ((status & DT3000_COMPLETION_MASK) != DT3000_NOERROR)
 296                dev_dbg(dev->class_dev, "%s: timeout/error status=0x%04x\n",
 297                        __func__, status);
 298}
 299
 300static unsigned int dt3k_readsingle(struct comedi_device *dev,
 301                                    unsigned int subsys, unsigned int chan,
 302                                    unsigned int gain)
 303{
 304        struct dt3k_private *devpriv = dev->private;
 305
 306        writew(subsys, devpriv->io_addr + DPR_SubSys);
 307
 308        writew(chan, devpriv->io_addr + DPR_Params(0));
 309        writew(gain, devpriv->io_addr + DPR_Params(1));
 310
 311        dt3k_send_cmd(dev, CMD_READSINGLE);
 312
 313        return readw(devpriv->io_addr + DPR_Params(2));
 314}
 315
 316static void dt3k_writesingle(struct comedi_device *dev, unsigned int subsys,
 317                             unsigned int chan, unsigned int data)
 318{
 319        struct dt3k_private *devpriv = dev->private;
 320
 321        writew(subsys, devpriv->io_addr + DPR_SubSys);
 322
 323        writew(chan, devpriv->io_addr + DPR_Params(0));
 324        writew(0, devpriv->io_addr + DPR_Params(1));
 325        writew(data, devpriv->io_addr + DPR_Params(2));
 326
 327        dt3k_send_cmd(dev, CMD_WRITESINGLE);
 328}
 329
 330static void dt3k_ai_empty_fifo(struct comedi_device *dev,
 331                               struct comedi_subdevice *s)
 332{
 333        struct dt3k_private *devpriv = dev->private;
 334        int front;
 335        int rear;
 336        int count;
 337        int i;
 338        short data;
 339
 340        front = readw(devpriv->io_addr + DPR_AD_Buf_Front);
 341        count = front - devpriv->ai_front;
 342        if (count < 0)
 343                count += AI_FIFO_DEPTH;
 344
 345        rear = devpriv->ai_rear;
 346
 347        for (i = 0; i < count; i++) {
 348                data = readw(devpriv->io_addr + DPR_ADC_buffer + rear);
 349                comedi_buf_put(s->async, data);
 350                rear++;
 351                if (rear >= AI_FIFO_DEPTH)
 352                        rear = 0;
 353        }
 354
 355        devpriv->ai_rear = rear;
 356        writew(rear, devpriv->io_addr + DPR_AD_Buf_Rear);
 357}
 358
 359static int dt3k_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 360{
 361        struct dt3k_private *devpriv = dev->private;
 362
 363        writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
 364        dt3k_send_cmd(dev, CMD_STOP);
 365
 366        writew(0, devpriv->io_addr + DPR_Int_Mask);
 367
 368        return 0;
 369}
 370
 371static int debug_n_ints;
 372
 373/* FIXME! Assumes shared interrupt is for this card. */
 374/* What's this debug_n_ints stuff? Obviously needs some work... */
 375static irqreturn_t dt3k_interrupt(int irq, void *d)
 376{
 377        struct comedi_device *dev = d;
 378        struct dt3k_private *devpriv = dev->private;
 379        struct comedi_subdevice *s;
 380        unsigned int status;
 381
 382        if (!dev->attached)
 383                return IRQ_NONE;
 384
 385        s = &dev->subdevices[0];
 386        status = readw(devpriv->io_addr + DPR_Intr_Flag);
 387#ifdef DEBUG
 388        debug_intr_flags(status);
 389#endif
 390
 391        if (status & DT3000_ADFULL) {
 392                dt3k_ai_empty_fifo(dev, s);
 393                s->async->events |= COMEDI_CB_BLOCK;
 394        }
 395
 396        if (status & (DT3000_ADSWERR | DT3000_ADHWERR))
 397                s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
 398
 399        debug_n_ints++;
 400        if (debug_n_ints >= 10) {
 401                dt3k_ai_cancel(dev, s);
 402                s->async->events |= COMEDI_CB_EOA;
 403        }
 404
 405        comedi_event(dev, s);
 406        return IRQ_HANDLED;
 407}
 408
 409static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
 410                            unsigned int round_mode)
 411{
 412        int divider, base, prescale;
 413
 414        /* This function needs improvment */
 415        /* Don't know if divider==0 works. */
 416
 417        for (prescale = 0; prescale < 16; prescale++) {
 418                base = timer_base * (prescale + 1);
 419                switch (round_mode) {
 420                case TRIG_ROUND_NEAREST:
 421                default:
 422                        divider = (*nanosec + base / 2) / base;
 423                        break;
 424                case TRIG_ROUND_DOWN:
 425                        divider = (*nanosec) / base;
 426                        break;
 427                case TRIG_ROUND_UP:
 428                        divider = (*nanosec) / base;
 429                        break;
 430                }
 431                if (divider < 65536) {
 432                        *nanosec = divider * base;
 433                        return (prescale << 16) | (divider);
 434                }
 435        }
 436
 437        prescale = 15;
 438        base = timer_base * (1 << prescale);
 439        divider = 65535;
 440        *nanosec = divider * base;
 441        return (prescale << 16) | (divider);
 442}
 443
 444static int dt3k_ai_cmdtest(struct comedi_device *dev,
 445                           struct comedi_subdevice *s, struct comedi_cmd *cmd)
 446{
 447        const struct dt3k_boardtype *this_board = comedi_board(dev);
 448        int err = 0;
 449        int tmp;
 450
 451        /* Step 1 : check if triggers are trivially valid */
 452
 453        err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
 454        err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
 455        err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
 456        err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 457        err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
 458
 459        if (err)
 460                return 1;
 461
 462        /* Step 2a : make sure trigger sources are unique */
 463        /* Step 2b : and mutually compatible */
 464
 465        if (err)
 466                return 2;
 467
 468        /* Step 3: check if arguments are trivially valid */
 469
 470        err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
 471
 472        if (cmd->scan_begin_src == TRIG_TIMER) {
 473                err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
 474                                                 this_board->ai_speed);
 475                err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg,
 476                                                 100 * 16 * 65535);
 477        }
 478
 479        if (cmd->convert_src == TRIG_TIMER) {
 480                err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
 481                                                 this_board->ai_speed);
 482                err |= cfc_check_trigger_arg_max(&cmd->convert_arg,
 483                                                 50 * 16 * 65535);
 484        }
 485
 486        err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
 487
 488        if (cmd->stop_src == TRIG_COUNT)
 489                err |= cfc_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff);
 490        else    /* TRIG_NONE */
 491                err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
 492
 493        if (err)
 494                return 3;
 495
 496        /* step 4: fix up any arguments */
 497
 498        if (cmd->scan_begin_src == TRIG_TIMER) {
 499                tmp = cmd->scan_begin_arg;
 500                dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
 501                                 cmd->flags & TRIG_ROUND_MASK);
 502                if (tmp != cmd->scan_begin_arg)
 503                        err++;
 504        }
 505
 506        if (cmd->convert_src == TRIG_TIMER) {
 507                tmp = cmd->convert_arg;
 508                dt3k_ns_to_timer(50, &cmd->convert_arg,
 509                                 cmd->flags & TRIG_ROUND_MASK);
 510                if (tmp != cmd->convert_arg)
 511                        err++;
 512                if (cmd->scan_begin_src == TRIG_TIMER &&
 513                    cmd->scan_begin_arg <
 514                    cmd->convert_arg * cmd->scan_end_arg) {
 515                        cmd->scan_begin_arg =
 516                            cmd->convert_arg * cmd->scan_end_arg;
 517                        err++;
 518                }
 519        }
 520
 521        if (err)
 522                return 4;
 523
 524        return 0;
 525}
 526
 527static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 528{
 529        struct dt3k_private *devpriv = dev->private;
 530        struct comedi_cmd *cmd = &s->async->cmd;
 531        int i;
 532        unsigned int chan, range, aref;
 533        unsigned int divider;
 534        unsigned int tscandiv;
 535        unsigned int mode;
 536
 537        for (i = 0; i < cmd->chanlist_len; i++) {
 538                chan = CR_CHAN(cmd->chanlist[i]);
 539                range = CR_RANGE(cmd->chanlist[i]);
 540
 541                writew((range << 6) | chan,
 542                       devpriv->io_addr + DPR_ADC_buffer + i);
 543        }
 544        aref = CR_AREF(cmd->chanlist[0]);
 545
 546        writew(cmd->scan_end_arg, devpriv->io_addr + DPR_Params(0));
 547
 548        if (cmd->convert_src == TRIG_TIMER) {
 549                divider = dt3k_ns_to_timer(50, &cmd->convert_arg,
 550                                           cmd->flags & TRIG_ROUND_MASK);
 551                writew((divider >> 16), devpriv->io_addr + DPR_Params(1));
 552                writew((divider & 0xffff), devpriv->io_addr + DPR_Params(2));
 553        }
 554
 555        if (cmd->scan_begin_src == TRIG_TIMER) {
 556                tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
 557                                            cmd->flags & TRIG_ROUND_MASK);
 558                writew((tscandiv >> 16), devpriv->io_addr + DPR_Params(3));
 559                writew((tscandiv & 0xffff), devpriv->io_addr + DPR_Params(4));
 560        }
 561
 562        mode = DT3000_AD_RETRIG_INTERNAL | 0 | 0;
 563        writew(mode, devpriv->io_addr + DPR_Params(5));
 564        writew(aref == AREF_DIFF, devpriv->io_addr + DPR_Params(6));
 565
 566        writew(AI_FIFO_DEPTH / 2, devpriv->io_addr + DPR_Params(7));
 567
 568        writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
 569        dt3k_send_cmd(dev, CMD_CONFIG);
 570
 571        writew(DT3000_ADFULL | DT3000_ADSWERR | DT3000_ADHWERR,
 572               devpriv->io_addr + DPR_Int_Mask);
 573
 574        debug_n_ints = 0;
 575
 576        writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
 577        dt3k_send_cmd(dev, CMD_START);
 578
 579        return 0;
 580}
 581
 582static int dt3k_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
 583                        struct comedi_insn *insn, unsigned int *data)
 584{
 585        int i;
 586        unsigned int chan, gain, aref;
 587
 588        chan = CR_CHAN(insn->chanspec);
 589        gain = CR_RANGE(insn->chanspec);
 590        /* XXX docs don't explain how to select aref */
 591        aref = CR_AREF(insn->chanspec);
 592
 593        for (i = 0; i < insn->n; i++)
 594                data[i] = dt3k_readsingle(dev, SUBS_AI, chan, gain);
 595
 596        return i;
 597}
 598
 599static int dt3k_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
 600                        struct comedi_insn *insn, unsigned int *data)
 601{
 602        struct dt3k_private *devpriv = dev->private;
 603        int i;
 604        unsigned int chan;
 605
 606        chan = CR_CHAN(insn->chanspec);
 607        for (i = 0; i < insn->n; i++) {
 608                dt3k_writesingle(dev, SUBS_AO, chan, data[i]);
 609                devpriv->ao_readback[chan] = data[i];
 610        }
 611
 612        return i;
 613}
 614
 615static int dt3k_ao_insn_read(struct comedi_device *dev,
 616                             struct comedi_subdevice *s,
 617                             struct comedi_insn *insn, unsigned int *data)
 618{
 619        struct dt3k_private *devpriv = dev->private;
 620        int i;
 621        unsigned int chan;
 622
 623        chan = CR_CHAN(insn->chanspec);
 624        for (i = 0; i < insn->n; i++)
 625                data[i] = devpriv->ao_readback[chan];
 626
 627        return i;
 628}
 629
 630static void dt3k_dio_config(struct comedi_device *dev, int bits)
 631{
 632        struct dt3k_private *devpriv = dev->private;
 633
 634        /* XXX */
 635        writew(SUBS_DOUT, devpriv->io_addr + DPR_SubSys);
 636
 637        writew(bits, devpriv->io_addr + DPR_Params(0));
 638#if 0
 639        /* don't know */
 640        writew(0, devpriv->io_addr + DPR_Params(1));
 641        writew(0, devpriv->io_addr + DPR_Params(2));
 642#endif
 643
 644        dt3k_send_cmd(dev, CMD_CONFIG);
 645}
 646
 647static int dt3k_dio_insn_config(struct comedi_device *dev,
 648                                struct comedi_subdevice *s,
 649                                struct comedi_insn *insn, unsigned int *data)
 650{
 651        int mask;
 652
 653        mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
 654
 655        switch (data[0]) {
 656        case INSN_CONFIG_DIO_OUTPUT:
 657                s->io_bits |= mask;
 658                break;
 659        case INSN_CONFIG_DIO_INPUT:
 660                s->io_bits &= ~mask;
 661                break;
 662        case INSN_CONFIG_DIO_QUERY:
 663                data[1] =
 664                    (s->
 665                     io_bits & (1 << CR_CHAN(insn->chanspec))) ? COMEDI_OUTPUT :
 666                    COMEDI_INPUT;
 667                return insn->n;
 668                break;
 669        default:
 670                return -EINVAL;
 671                break;
 672        }
 673        mask = (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3);
 674        dt3k_dio_config(dev, mask);
 675
 676        return insn->n;
 677}
 678
 679static int dt3k_dio_insn_bits(struct comedi_device *dev,
 680                              struct comedi_subdevice *s,
 681                              struct comedi_insn *insn, unsigned int *data)
 682{
 683        if (data[0]) {
 684                s->state &= ~data[0];
 685                s->state |= data[1] & data[0];
 686                dt3k_writesingle(dev, SUBS_DOUT, 0, s->state);
 687        }
 688        data[1] = dt3k_readsingle(dev, SUBS_DIN, 0, 0);
 689
 690        return insn->n;
 691}
 692
 693static int dt3k_mem_insn_read(struct comedi_device *dev,
 694                              struct comedi_subdevice *s,
 695                              struct comedi_insn *insn, unsigned int *data)
 696{
 697        struct dt3k_private *devpriv = dev->private;
 698        unsigned int addr = CR_CHAN(insn->chanspec);
 699        int i;
 700
 701        for (i = 0; i < insn->n; i++) {
 702                writew(SUBS_MEM, devpriv->io_addr + DPR_SubSys);
 703                writew(addr, devpriv->io_addr + DPR_Params(0));
 704                writew(1, devpriv->io_addr + DPR_Params(1));
 705
 706                dt3k_send_cmd(dev, CMD_READCODE);
 707
 708                data[i] = readw(devpriv->io_addr + DPR_Params(2));
 709        }
 710
 711        return i;
 712}
 713
 714static int dt3000_auto_attach(struct comedi_device *dev,
 715                              unsigned long context)
 716{
 717        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 718        const struct dt3k_boardtype *this_board = NULL;
 719        struct dt3k_private *devpriv;
 720        struct comedi_subdevice *s;
 721        int ret = 0;
 722
 723        if (context < ARRAY_SIZE(dt3k_boardtypes))
 724                this_board = &dt3k_boardtypes[context];
 725        if (!this_board)
 726                return -ENODEV;
 727        dev->board_ptr = this_board;
 728        dev->board_name = this_board->name;
 729
 730        devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
 731        if (!devpriv)
 732                return -ENOMEM;
 733        dev->private = devpriv;
 734
 735        ret = comedi_pci_enable(dev);
 736        if (ret < 0)
 737                return ret;
 738
 739        devpriv->io_addr = pci_ioremap_bar(pcidev, 0);
 740        if (!devpriv->io_addr)
 741                return -ENOMEM;
 742
 743        ret = request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED,
 744                          dev->board_name, dev);
 745        if (ret)
 746                return ret;
 747        dev->irq = pcidev->irq;
 748
 749        ret = comedi_alloc_subdevices(dev, 4);
 750        if (ret)
 751                return ret;
 752
 753        s = &dev->subdevices[0];
 754        dev->read_subdev = s;
 755        /* ai subdevice */
 756        s->type         = COMEDI_SUBD_AI;
 757        s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
 758        s->n_chan       = this_board->adchan;
 759        s->insn_read    = dt3k_ai_insn;
 760        s->maxdata      = (1 << this_board->adbits) - 1;
 761        s->len_chanlist = 512;
 762        s->range_table  = &range_dt3000_ai;     /* XXX */
 763        s->do_cmd       = dt3k_ai_cmd;
 764        s->do_cmdtest   = dt3k_ai_cmdtest;
 765        s->cancel       = dt3k_ai_cancel;
 766
 767        s = &dev->subdevices[1];
 768        /* ao subsystem */
 769        s->type         = COMEDI_SUBD_AO;
 770        s->subdev_flags = SDF_WRITABLE;
 771        s->n_chan       = 2;
 772        s->insn_read    = dt3k_ao_insn_read;
 773        s->insn_write   = dt3k_ao_insn;
 774        s->maxdata      = (1 << this_board->dabits) - 1;
 775        s->len_chanlist = 1;
 776        s->range_table  = &range_bipolar10;
 777
 778        s = &dev->subdevices[2];
 779        /* dio subsystem */
 780        s->type         = COMEDI_SUBD_DIO;
 781        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 782        s->n_chan       = 8;
 783        s->insn_config  = dt3k_dio_insn_config;
 784        s->insn_bits    = dt3k_dio_insn_bits;
 785        s->maxdata      = 1;
 786        s->len_chanlist = 8;
 787        s->range_table  = &range_digital;
 788
 789        s = &dev->subdevices[3];
 790        /* mem subsystem */
 791        s->type         = COMEDI_SUBD_MEMORY;
 792        s->subdev_flags = SDF_READABLE;
 793        s->n_chan       = 0x1000;
 794        s->insn_read    = dt3k_mem_insn_read;
 795        s->maxdata      = 0xff;
 796        s->len_chanlist = 1;
 797        s->range_table  = &range_unknown;
 798
 799#if 0
 800        s = &dev->subdevices[4];
 801        /* proc subsystem */
 802        s->type = COMEDI_SUBD_PROC;
 803#endif
 804
 805        dev_info(dev->class_dev, "%s attached\n", dev->board_name);
 806
 807        return 0;
 808}
 809
 810static void dt3000_detach(struct comedi_device *dev)
 811{
 812        struct dt3k_private *devpriv = dev->private;
 813
 814        if (dev->irq)
 815                free_irq(dev->irq, dev);
 816        if (devpriv) {
 817                if (devpriv->io_addr)
 818                        iounmap(devpriv->io_addr);
 819        }
 820        comedi_pci_disable(dev);
 821}
 822
 823static struct comedi_driver dt3000_driver = {
 824        .driver_name    = "dt3000",
 825        .module         = THIS_MODULE,
 826        .auto_attach    = dt3000_auto_attach,
 827        .detach         = dt3000_detach,
 828};
 829
 830static int dt3000_pci_probe(struct pci_dev *dev,
 831                            const struct pci_device_id *id)
 832{
 833        return comedi_pci_auto_config(dev, &dt3000_driver, id->driver_data);
 834}
 835
 836static DEFINE_PCI_DEVICE_TABLE(dt3000_pci_table) = {
 837        { PCI_VDEVICE(DT, 0x0022), BOARD_DT3001 },
 838        { PCI_VDEVICE(DT, 0x0023), BOARD_DT3002 },
 839        { PCI_VDEVICE(DT, 0x0024), BOARD_DT3003 },
 840        { PCI_VDEVICE(DT, 0x0025), BOARD_DT3004 },
 841        { PCI_VDEVICE(DT, 0x0026), BOARD_DT3005 },
 842        { PCI_VDEVICE(DT, 0x0027), BOARD_DT3001_PGL },
 843        { PCI_VDEVICE(DT, 0x0028), BOARD_DT3003_PGL },
 844        { 0 }
 845};
 846MODULE_DEVICE_TABLE(pci, dt3000_pci_table);
 847
 848static struct pci_driver dt3000_pci_driver = {
 849        .name           = "dt3000",
 850        .id_table       = dt3000_pci_table,
 851        .probe          = dt3000_pci_probe,
 852        .remove         = comedi_pci_auto_unconfig,
 853};
 854module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver);
 855
 856MODULE_AUTHOR("Comedi http://www.comedi.org");
 857MODULE_DESCRIPTION("Comedi low-level driver");
 858MODULE_LICENSE("GPL");
 859