linux/drivers/staging/comedi/drivers/me_daq.c
<<
>>
Prefs
   1/*
   2 * comedi/drivers/me_daq.c
   3 * Hardware driver for Meilhaus data acquisition cards:
   4 *   ME-2000i, ME-2600i, ME-3000vm1
   5 *
   6 * Copyright (C) 2002 Michael Hillmann <hillmann@syscongroup.de>
   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
  19/*
  20 * Driver: me_daq
  21 * Description: Meilhaus PCI data acquisition cards
  22 * Devices: (Meilhaus) ME-2600i [me-2600i]
  23 *          (Meilhaus) ME-2000i [me-2000i]
  24 * Author: Michael Hillmann <hillmann@syscongroup.de>
  25 * Status: experimental
  26 *
  27 * Configuration options: not applicable, uses PCI auto config
  28 *
  29 * Supports:
  30 *    Analog Input, Analog Output, Digital I/O
  31 */
  32
  33#include <linux/module.h>
  34#include <linux/pci.h>
  35#include <linux/interrupt.h>
  36#include <linux/sched.h>
  37
  38#include "../comedidev.h"
  39
  40#include "plx9052.h"
  41
  42#define ME2600_FIRMWARE         "me2600_firmware.bin"
  43
  44#define XILINX_DOWNLOAD_RESET   0x42    /* Xilinx registers */
  45
  46#define ME_CONTROL_1                    0x0000  /* - | W */
  47#define   INTERRUPT_ENABLE              (1<<15)
  48#define   COUNTER_B_IRQ                 (1<<12)
  49#define   COUNTER_A_IRQ                 (1<<11)
  50#define   CHANLIST_READY_IRQ            (1<<10)
  51#define   EXT_IRQ                       (1<<9)
  52#define   ADFIFO_HALFFULL_IRQ           (1<<8)
  53#define   SCAN_COUNT_ENABLE             (1<<5)
  54#define   SIMULTANEOUS_ENABLE           (1<<4)
  55#define   TRIGGER_FALLING_EDGE          (1<<3)
  56#define   CONTINUOUS_MODE               (1<<2)
  57#define   DISABLE_ADC                   (0<<0)
  58#define   SOFTWARE_TRIGGERED_ADC        (1<<0)
  59#define   SCAN_TRIGGERED_ADC            (2<<0)
  60#define   EXT_TRIGGERED_ADC             (3<<0)
  61#define ME_ADC_START                    0x0000  /* R | - */
  62#define ME_CONTROL_2                    0x0002  /* - | W */
  63#define   ENABLE_ADFIFO                 (1<<10)
  64#define   ENABLE_CHANLIST               (1<<9)
  65#define   ENABLE_PORT_B                 (1<<7)
  66#define   ENABLE_PORT_A                 (1<<6)
  67#define   ENABLE_COUNTER_B              (1<<4)
  68#define   ENABLE_COUNTER_A              (1<<3)
  69#define   ENABLE_DAC                    (1<<1)
  70#define   BUFFERED_DAC                  (1<<0)
  71#define ME_DAC_UPDATE                   0x0002  /* R | - */
  72#define ME_STATUS                       0x0004  /* R | - */
  73#define   COUNTER_B_IRQ_PENDING         (1<<12)
  74#define   COUNTER_A_IRQ_PENDING         (1<<11)
  75#define   CHANLIST_READY_IRQ_PENDING    (1<<10)
  76#define   EXT_IRQ_PENDING               (1<<9)
  77#define   ADFIFO_HALFFULL_IRQ_PENDING   (1<<8)
  78#define   ADFIFO_FULL                   (1<<4)
  79#define   ADFIFO_HALFFULL               (1<<3)
  80#define   ADFIFO_EMPTY                  (1<<2)
  81#define   CHANLIST_FULL                 (1<<1)
  82#define   FST_ACTIVE                    (1<<0)
  83#define ME_RESET_INTERRUPT              0x0004  /* - | W */
  84#define ME_DIO_PORT_A                   0x0006  /* R | W */
  85#define ME_DIO_PORT_B                   0x0008  /* R | W */
  86#define ME_TIMER_DATA_0                 0x000A  /* - | W */
  87#define ME_TIMER_DATA_1                 0x000C  /* - | W */
  88#define ME_TIMER_DATA_2                 0x000E  /* - | W */
  89#define ME_CHANNEL_LIST                 0x0010  /* - | W */
  90#define   ADC_UNIPOLAR                  (1<<6)
  91#define   ADC_GAIN_0                    (0<<4)
  92#define   ADC_GAIN_1                    (1<<4)
  93#define   ADC_GAIN_2                    (2<<4)
  94#define   ADC_GAIN_3                    (3<<4)
  95#define ME_READ_AD_FIFO                 0x0010  /* R | - */
  96#define ME_DAC_CONTROL                  0x0012  /* - | W */
  97#define   DAC_UNIPOLAR_D                (0<<4)
  98#define   DAC_BIPOLAR_D                 (1<<4)
  99#define   DAC_UNIPOLAR_C                (0<<5)
 100#define   DAC_BIPOLAR_C                 (1<<5)
 101#define   DAC_UNIPOLAR_B                (0<<6)
 102#define   DAC_BIPOLAR_B                 (1<<6)
 103#define   DAC_UNIPOLAR_A                (0<<7)
 104#define   DAC_BIPOLAR_A                 (1<<7)
 105#define   DAC_GAIN_0_D                  (0<<8)
 106#define   DAC_GAIN_1_D                  (1<<8)
 107#define   DAC_GAIN_0_C                  (0<<9)
 108#define   DAC_GAIN_1_C                  (1<<9)
 109#define   DAC_GAIN_0_B                  (0<<10)
 110#define   DAC_GAIN_1_B                  (1<<10)
 111#define   DAC_GAIN_0_A                  (0<<11)
 112#define   DAC_GAIN_1_A                  (1<<11)
 113#define ME_DAC_CONTROL_UPDATE           0x0012  /* R | - */
 114#define ME_DAC_DATA_A                   0x0014  /* - | W */
 115#define ME_DAC_DATA_B                   0x0016  /* - | W */
 116#define ME_DAC_DATA_C                   0x0018  /* - | W */
 117#define ME_DAC_DATA_D                   0x001A  /* - | W */
 118#define ME_COUNTER_ENDDATA_A            0x001C  /* - | W */
 119#define ME_COUNTER_ENDDATA_B            0x001E  /* - | W */
 120#define ME_COUNTER_STARTDATA_A          0x0020  /* - | W */
 121#define ME_COUNTER_VALUE_A              0x0020  /* R | - */
 122#define ME_COUNTER_STARTDATA_B          0x0022  /* - | W */
 123#define ME_COUNTER_VALUE_B              0x0022  /* R | - */
 124
 125static const struct comedi_lrange me_ai_range = {
 126        8, {
 127                BIP_RANGE(10),
 128                BIP_RANGE(5),
 129                BIP_RANGE(2.5),
 130                BIP_RANGE(1.25),
 131                UNI_RANGE(10),
 132                UNI_RANGE(5),
 133                UNI_RANGE(2.5),
 134                UNI_RANGE(1.25)
 135        }
 136};
 137
 138static const struct comedi_lrange me_ao_range = {
 139        3, {
 140                BIP_RANGE(10),
 141                BIP_RANGE(5),
 142                UNI_RANGE(10)
 143        }
 144};
 145
 146enum me_boardid {
 147        BOARD_ME2600,
 148        BOARD_ME2000,
 149};
 150
 151struct me_board {
 152        const char *name;
 153        int needs_firmware;
 154        int has_ao;
 155};
 156
 157static const struct me_board me_boards[] = {
 158        [BOARD_ME2600] = {
 159                .name           = "me-2600i",
 160                .needs_firmware = 1,
 161                .has_ao         = 1,
 162        },
 163        [BOARD_ME2000] = {
 164                .name           = "me-2000i",
 165        },
 166};
 167
 168struct me_private_data {
 169        void __iomem *plx_regbase;      /* PLX configuration base address */
 170
 171        unsigned short control_1;       /* Mirror of CONTROL_1 register */
 172        unsigned short control_2;       /* Mirror of CONTROL_2 register */
 173        unsigned short dac_control;     /* Mirror of the DAC_CONTROL register */
 174};
 175
 176static inline void sleep(unsigned sec)
 177{
 178        current->state = TASK_INTERRUPTIBLE;
 179        schedule_timeout(sec * HZ);
 180}
 181
 182static int me_dio_insn_config(struct comedi_device *dev,
 183                              struct comedi_subdevice *s,
 184                              struct comedi_insn *insn,
 185                              unsigned int *data)
 186{
 187        struct me_private_data *devpriv = dev->private;
 188        unsigned int chan = CR_CHAN(insn->chanspec);
 189        unsigned int mask;
 190        int ret;
 191
 192        if (chan < 16)
 193                mask = 0x0000ffff;
 194        else
 195                mask = 0xffff0000;
 196
 197        ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 198        if (ret)
 199                return ret;
 200
 201        if (s->io_bits & 0x0000ffff)
 202                devpriv->control_2 |= ENABLE_PORT_A;
 203        else
 204                devpriv->control_2 &= ~ENABLE_PORT_A;
 205        if (s->io_bits & 0xffff0000)
 206                devpriv->control_2 |= ENABLE_PORT_B;
 207        else
 208                devpriv->control_2 &= ~ENABLE_PORT_B;
 209
 210        writew(devpriv->control_2, dev->mmio + ME_CONTROL_2);
 211
 212        return insn->n;
 213}
 214
 215static int me_dio_insn_bits(struct comedi_device *dev,
 216                            struct comedi_subdevice *s,
 217                            struct comedi_insn *insn,
 218                            unsigned int *data)
 219{
 220        void __iomem *mmio_porta = dev->mmio + ME_DIO_PORT_A;
 221        void __iomem *mmio_portb = dev->mmio + ME_DIO_PORT_B;
 222        unsigned int mask;
 223        unsigned int val;
 224
 225        mask = comedi_dio_update_state(s, data);
 226        if (mask) {
 227                if (mask & 0x0000ffff)
 228                        writew((s->state & 0xffff), mmio_porta);
 229                if (mask & 0xffff0000)
 230                        writew(((s->state >> 16) & 0xffff), mmio_portb);
 231        }
 232
 233        if (s->io_bits & 0x0000ffff)
 234                val = s->state & 0xffff;
 235        else
 236                val = readw(mmio_porta);
 237
 238        if (s->io_bits & 0xffff0000)
 239                val |= (s->state & 0xffff0000);
 240        else
 241                val |= (readw(mmio_portb) << 16);
 242
 243        data[1] = val;
 244
 245        return insn->n;
 246}
 247
 248static int me_ai_eoc(struct comedi_device *dev,
 249                     struct comedi_subdevice *s,
 250                     struct comedi_insn *insn,
 251                     unsigned long context)
 252{
 253        unsigned int status;
 254
 255        status = readw(dev->mmio + ME_STATUS);
 256        if ((status & 0x0004) == 0)
 257                return 0;
 258        return -EBUSY;
 259}
 260
 261static int me_ai_insn_read(struct comedi_device *dev,
 262                           struct comedi_subdevice *s,
 263                           struct comedi_insn *insn,
 264                           unsigned int *data)
 265{
 266        struct me_private_data *dev_private = dev->private;
 267        unsigned int chan = CR_CHAN(insn->chanspec);
 268        unsigned int rang = CR_RANGE(insn->chanspec);
 269        unsigned int aref = CR_AREF(insn->chanspec);
 270        unsigned short val;
 271        int ret;
 272
 273        /* stop any running conversion */
 274        dev_private->control_1 &= 0xFFFC;
 275        writew(dev_private->control_1, dev->mmio + ME_CONTROL_1);
 276
 277        /* clear chanlist and ad fifo */
 278        dev_private->control_2 &= ~(ENABLE_ADFIFO | ENABLE_CHANLIST);
 279        writew(dev_private->control_2, dev->mmio + ME_CONTROL_2);
 280
 281        /* reset any pending interrupt */
 282        writew(0x00, dev->mmio + ME_RESET_INTERRUPT);
 283
 284        /* enable the chanlist and ADC fifo */
 285        dev_private->control_2 |= (ENABLE_ADFIFO | ENABLE_CHANLIST);
 286        writew(dev_private->control_2, dev->mmio + ME_CONTROL_2);
 287
 288        /* write to channel list fifo */
 289        val = chan & 0x0f;                      /* b3:b0 channel */
 290        val |= (rang & 0x03) << 4;              /* b5:b4 gain */
 291        val |= (rang & 0x04) << 4;              /* b6 polarity */
 292        val |= ((aref & AREF_DIFF) ? 0x80 : 0); /* b7 differential */
 293        writew(val & 0xff, dev->mmio + ME_CHANNEL_LIST);
 294
 295        /* set ADC mode to software trigger */
 296        dev_private->control_1 |= SOFTWARE_TRIGGERED_ADC;
 297        writew(dev_private->control_1, dev->mmio + ME_CONTROL_1);
 298
 299        /* start conversion by reading from ADC_START */
 300        readw(dev->mmio + ME_ADC_START);
 301
 302        /* wait for ADC fifo not empty flag */
 303        ret = comedi_timeout(dev, s, insn, me_ai_eoc, 0);
 304        if (ret)
 305                return ret;
 306
 307        /* get value from ADC fifo */
 308        val = readw(dev->mmio + ME_READ_AD_FIFO);
 309        val = (val ^ 0x800) & 0x0fff;
 310        data[0] = val;
 311
 312        /* stop any running conversion */
 313        dev_private->control_1 &= 0xFFFC;
 314        writew(dev_private->control_1, dev->mmio + ME_CONTROL_1);
 315
 316        return 1;
 317}
 318
 319static int me_ao_insn_write(struct comedi_device *dev,
 320                            struct comedi_subdevice *s,
 321                            struct comedi_insn *insn,
 322                            unsigned int *data)
 323{
 324        struct me_private_data *dev_private = dev->private;
 325        unsigned int chan = CR_CHAN(insn->chanspec);
 326        unsigned int rang = CR_RANGE(insn->chanspec);
 327        unsigned int val = s->readback[chan];
 328        int i;
 329
 330        /* Enable all DAC */
 331        dev_private->control_2 |= ENABLE_DAC;
 332        writew(dev_private->control_2, dev->mmio + ME_CONTROL_2);
 333
 334        /* and set DAC to "buffered" mode */
 335        dev_private->control_2 |= BUFFERED_DAC;
 336        writew(dev_private->control_2, dev->mmio + ME_CONTROL_2);
 337
 338        /* Set dac-control register */
 339        for (i = 0; i < insn->n; i++) {
 340                /* clear bits for this channel */
 341                dev_private->dac_control &= ~(0x0880 >> chan);
 342                if (rang == 0)
 343                        dev_private->dac_control |=
 344                            ((DAC_BIPOLAR_A | DAC_GAIN_1_A) >> chan);
 345                else if (rang == 1)
 346                        dev_private->dac_control |=
 347                            ((DAC_BIPOLAR_A | DAC_GAIN_0_A) >> chan);
 348        }
 349        writew(dev_private->dac_control, dev->mmio + ME_DAC_CONTROL);
 350
 351        /* Update dac-control register */
 352        readw(dev->mmio + ME_DAC_CONTROL_UPDATE);
 353
 354        /* Set data register */
 355        for (i = 0; i < insn->n; i++) {
 356                val = data[i];
 357
 358                writew(val, dev->mmio + ME_DAC_DATA_A + (chan << 1));
 359        }
 360        s->readback[chan] = val;
 361
 362        /* Update dac with data registers */
 363        readw(dev->mmio + ME_DAC_UPDATE);
 364
 365        return insn->n;
 366}
 367
 368static int me2600_xilinx_download(struct comedi_device *dev,
 369                                  const u8 *data, size_t size,
 370                                  unsigned long context)
 371{
 372        struct me_private_data *dev_private = dev->private;
 373        unsigned int value;
 374        unsigned int file_length;
 375        unsigned int i;
 376
 377        /* disable irq's on PLX */
 378        writel(0x00, dev_private->plx_regbase + PLX9052_INTCSR);
 379
 380        /* First, make a dummy read to reset xilinx */
 381        value = readw(dev->mmio + XILINX_DOWNLOAD_RESET);
 382
 383        /* Wait until reset is over */
 384        sleep(1);
 385
 386        /* Write a dummy value to Xilinx */
 387        writeb(0x00, dev->mmio + 0x0);
 388        sleep(1);
 389
 390        /*
 391         * Format of the firmware
 392         * Build longs from the byte-wise coded header
 393         * Byte 1-3:   length of the array
 394         * Byte 4-7:   version
 395         * Byte 8-11:  date
 396         * Byte 12-15: reserved
 397         */
 398        if (size < 16)
 399                return -EINVAL;
 400
 401        file_length = (((unsigned int)data[0] & 0xff) << 24) +
 402            (((unsigned int)data[1] & 0xff) << 16) +
 403            (((unsigned int)data[2] & 0xff) << 8) +
 404            ((unsigned int)data[3] & 0xff);
 405
 406        /*
 407         * Loop for writing firmware byte by byte to xilinx
 408         * Firmware data start at offset 16
 409         */
 410        for (i = 0; i < file_length; i++)
 411                writeb((data[16 + i] & 0xff), dev->mmio + 0x0);
 412
 413        /* Write 5 dummy values to xilinx */
 414        for (i = 0; i < 5; i++)
 415                writeb(0x00, dev->mmio + 0x0);
 416
 417        /* Test if there was an error during download -> INTB was thrown */
 418        value = readl(dev_private->plx_regbase + PLX9052_INTCSR);
 419        if (value & PLX9052_INTCSR_LI2STAT) {
 420                /* Disable interrupt */
 421                writel(0x00, dev_private->plx_regbase + PLX9052_INTCSR);
 422                dev_err(dev->class_dev, "Xilinx download failed\n");
 423                return -EIO;
 424        }
 425
 426        /* Wait until the Xilinx is ready for real work */
 427        sleep(1);
 428
 429        /* Enable PLX-Interrupts */
 430        writel(PLX9052_INTCSR_LI1ENAB |
 431               PLX9052_INTCSR_LI1POL |
 432               PLX9052_INTCSR_PCIENAB,
 433               dev_private->plx_regbase + PLX9052_INTCSR);
 434
 435        return 0;
 436}
 437
 438static int me_reset(struct comedi_device *dev)
 439{
 440        struct me_private_data *dev_private = dev->private;
 441
 442        /* Reset board */
 443        writew(0x00, dev->mmio + ME_CONTROL_1);
 444        writew(0x00, dev->mmio + ME_CONTROL_2);
 445        writew(0x00, dev->mmio + ME_RESET_INTERRUPT);
 446        writew(0x00, dev->mmio + ME_DAC_CONTROL);
 447
 448        /* Save values in the board context */
 449        dev_private->dac_control = 0;
 450        dev_private->control_1 = 0;
 451        dev_private->control_2 = 0;
 452
 453        return 0;
 454}
 455
 456static int me_auto_attach(struct comedi_device *dev,
 457                          unsigned long context)
 458{
 459        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 460        const struct me_board *board = NULL;
 461        struct me_private_data *dev_private;
 462        struct comedi_subdevice *s;
 463        int ret;
 464
 465        if (context < ARRAY_SIZE(me_boards))
 466                board = &me_boards[context];
 467        if (!board)
 468                return -ENODEV;
 469        dev->board_ptr = board;
 470        dev->board_name = board->name;
 471
 472        dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
 473        if (!dev_private)
 474                return -ENOMEM;
 475
 476        ret = comedi_pci_enable(dev);
 477        if (ret)
 478                return ret;
 479
 480        dev_private->plx_regbase = pci_ioremap_bar(pcidev, 0);
 481        if (!dev_private->plx_regbase)
 482                return -ENOMEM;
 483
 484        dev->mmio = pci_ioremap_bar(pcidev, 2);
 485        if (!dev->mmio)
 486                return -ENOMEM;
 487
 488        /* Download firmware and reset card */
 489        if (board->needs_firmware) {
 490                ret = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev,
 491                                           ME2600_FIRMWARE,
 492                                           me2600_xilinx_download, 0);
 493                if (ret < 0)
 494                        return ret;
 495        }
 496        me_reset(dev);
 497
 498        ret = comedi_alloc_subdevices(dev, 3);
 499        if (ret)
 500                return ret;
 501
 502        s = &dev->subdevices[0];
 503        s->type         = COMEDI_SUBD_AI;
 504        s->subdev_flags = SDF_READABLE | SDF_COMMON;
 505        s->n_chan       = 16;
 506        s->maxdata      = 0x0fff;
 507        s->len_chanlist = 16;
 508        s->range_table  = &me_ai_range;
 509        s->insn_read    = me_ai_insn_read;
 510
 511        s = &dev->subdevices[1];
 512        if (board->has_ao) {
 513                s->type         = COMEDI_SUBD_AO;
 514                s->subdev_flags = SDF_WRITABLE | SDF_COMMON;
 515                s->n_chan       = 4;
 516                s->maxdata      = 0x0fff;
 517                s->len_chanlist = 4;
 518                s->range_table  = &me_ao_range;
 519                s->insn_write   = me_ao_insn_write;
 520
 521                ret = comedi_alloc_subdev_readback(s);
 522                if (ret)
 523                        return ret;
 524        } else {
 525                s->type = COMEDI_SUBD_UNUSED;
 526        }
 527
 528        s = &dev->subdevices[2];
 529        s->type         = COMEDI_SUBD_DIO;
 530        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 531        s->n_chan       = 32;
 532        s->maxdata      = 1;
 533        s->len_chanlist = 32;
 534        s->range_table  = &range_digital;
 535        s->insn_bits    = me_dio_insn_bits;
 536        s->insn_config  = me_dio_insn_config;
 537
 538        return 0;
 539}
 540
 541static void me_detach(struct comedi_device *dev)
 542{
 543        struct me_private_data *dev_private = dev->private;
 544
 545        if (dev_private) {
 546                if (dev->mmio)
 547                        me_reset(dev);
 548                if (dev_private->plx_regbase)
 549                        iounmap(dev_private->plx_regbase);
 550        }
 551        comedi_pci_detach(dev);
 552}
 553
 554static struct comedi_driver me_daq_driver = {
 555        .driver_name    = "me_daq",
 556        .module         = THIS_MODULE,
 557        .auto_attach    = me_auto_attach,
 558        .detach         = me_detach,
 559};
 560
 561static int me_daq_pci_probe(struct pci_dev *dev,
 562                            const struct pci_device_id *id)
 563{
 564        return comedi_pci_auto_config(dev, &me_daq_driver, id->driver_data);
 565}
 566
 567static const struct pci_device_id me_daq_pci_table[] = {
 568        { PCI_VDEVICE(MEILHAUS, 0x2600), BOARD_ME2600 },
 569        { PCI_VDEVICE(MEILHAUS, 0x2000), BOARD_ME2000 },
 570        { 0 }
 571};
 572MODULE_DEVICE_TABLE(pci, me_daq_pci_table);
 573
 574static struct pci_driver me_daq_pci_driver = {
 575        .name           = "me_daq",
 576        .id_table       = me_daq_pci_table,
 577        .probe          = me_daq_pci_probe,
 578        .remove         = comedi_pci_auto_unconfig,
 579};
 580module_comedi_pci_driver(me_daq_driver, me_daq_pci_driver);
 581
 582MODULE_AUTHOR("Comedi http://www.comedi.org");
 583MODULE_DESCRIPTION("Comedi low-level driver");
 584MODULE_LICENSE("GPL");
 585MODULE_FIRMWARE(ME2600_FIRMWARE);
 586
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.