linux/drivers/staging/comedi/drivers/mf6x4.c
<<
>>
Prefs
   1/*
   2 *  comedi/drivers/mf6x4.c
   3 *  Driver for Humusoft MF634 and MF624 Data acquisition cards
   4 *
   5 *  COMEDI - Linux Control and Measurement Device Interface
   6 *  Copyright (C) 2000 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/*
  19 * Driver: mf6x4
  20 * Description: Humusoft MF634 and MF624 Data acquisition card driver
  21 * Devices: [Humusoft] MF634 (mf634), MF624 (mf624)
  22 * Author: Rostislav Lisovy <lisovy@gmail.com>
  23 * Status: works
  24 * Updated:
  25 * Configuration Options: none
  26 */
  27
  28#include <linux/module.h>
  29#include <linux/delay.h>
  30
  31#include "../comedi_pci.h"
  32
  33/* Registers present in BAR0 memory region */
  34#define MF624_GPIOC_REG         0x54
  35
  36#define MF6X4_GPIOC_EOLC        BIT(17) /* End Of Last Conversion */
  37#define MF6X4_GPIOC_LDAC        BIT(23) /* Load DACs */
  38#define MF6X4_GPIOC_DACEN       BIT(26)
  39
  40/* BAR1 registers */
  41#define MF6X4_ADDATA_REG        0x00
  42#define MF6X4_ADCTRL_REG        0x00
  43#define MF6X4_ADCTRL_CHAN(x)    BIT(chan)
  44#define MF6X4_DIN_REG           0x10
  45#define MF6X4_DIN_MASK          0xff
  46#define MF6X4_DOUT_REG          0x10
  47#define MF6X4_ADSTART_REG       0x20
  48#define MF6X4_DAC_REG(x)        (0x20 + ((x) * 2))
  49
  50/* BAR2 registers */
  51#define MF634_GPIOC_REG         0x68
  52
  53enum mf6x4_boardid {
  54        BOARD_MF634,
  55        BOARD_MF624,
  56};
  57
  58struct mf6x4_board {
  59        const char *name;
  60        /* We need to keep track of the order of BARs used by the cards */
  61        unsigned int bar_nums[3];
  62};
  63
  64static const struct mf6x4_board mf6x4_boards[] = {
  65        [BOARD_MF634] = {
  66                .name           = "mf634",
  67                .bar_nums       = {0, 2, 3},
  68        },
  69        [BOARD_MF624] = {
  70                .name           = "mf624",
  71                .bar_nums       = {0, 2, 4},
  72        },
  73};
  74
  75struct mf6x4_private {
  76        /*
  77         * Documentation for both MF634 and MF624 describes registers
  78         * present in BAR0, 1 and 2 regions.
  79         * The real (i.e. in HW) BAR numbers are different for MF624
  80         * and MF634 yet we will call them 0, 1, 2
  81         */
  82        void __iomem *bar0_mem;
  83        void __iomem *bar2_mem;
  84
  85        /*
  86         * This configuration register has the same function and fields
  87         * for both cards however it lies in different BARs on different
  88         * offsets -- this variable makes the access easier
  89         */
  90        void __iomem *gpioc_reg;
  91};
  92
  93static int mf6x4_di_insn_bits(struct comedi_device *dev,
  94                              struct comedi_subdevice *s,
  95                              struct comedi_insn *insn,
  96                              unsigned int *data)
  97{
  98        data[1] = ioread16(dev->mmio + MF6X4_DIN_REG) & MF6X4_DIN_MASK;
  99
 100        return insn->n;
 101}
 102
 103static int mf6x4_do_insn_bits(struct comedi_device *dev,
 104                              struct comedi_subdevice *s,
 105                              struct comedi_insn *insn,
 106                              unsigned int *data)
 107{
 108        if (comedi_dio_update_state(s, data))
 109                iowrite16(s->state, dev->mmio + MF6X4_DOUT_REG);
 110
 111        data[1] = s->state;
 112
 113        return insn->n;
 114}
 115
 116static int mf6x4_ai_eoc(struct comedi_device *dev,
 117                        struct comedi_subdevice *s,
 118                        struct comedi_insn *insn,
 119                        unsigned long context)
 120{
 121        struct mf6x4_private *devpriv = dev->private;
 122        unsigned int status;
 123
 124        status = ioread32(devpriv->gpioc_reg);
 125        if (status & MF6X4_GPIOC_EOLC)
 126                return 0;
 127        return -EBUSY;
 128}
 129
 130static int mf6x4_ai_insn_read(struct comedi_device *dev,
 131                              struct comedi_subdevice *s,
 132                              struct comedi_insn *insn,
 133                              unsigned int *data)
 134{
 135        unsigned int chan = CR_CHAN(insn->chanspec);
 136        unsigned int d;
 137        int ret;
 138        int i;
 139
 140        /* Set the ADC channel number in the scan list */
 141        iowrite16(MF6X4_ADCTRL_CHAN(chan), dev->mmio + MF6X4_ADCTRL_REG);
 142
 143        for (i = 0; i < insn->n; i++) {
 144                /* Trigger ADC conversion by reading ADSTART */
 145                ioread16(dev->mmio + MF6X4_ADSTART_REG);
 146
 147                ret = comedi_timeout(dev, s, insn, mf6x4_ai_eoc, 0);
 148                if (ret)
 149                        return ret;
 150
 151                /* Read the actual value */
 152                d = ioread16(dev->mmio + MF6X4_ADDATA_REG);
 153                d &= s->maxdata;
 154                /* munge the 2's complement data to offset binary */
 155                data[i] = comedi_offset_munge(s, d);
 156        }
 157
 158        iowrite16(0x0, dev->mmio + MF6X4_ADCTRL_REG);
 159
 160        return insn->n;
 161}
 162
 163static int mf6x4_ao_insn_write(struct comedi_device *dev,
 164                               struct comedi_subdevice *s,
 165                               struct comedi_insn *insn,
 166                               unsigned int *data)
 167{
 168        struct mf6x4_private *devpriv = dev->private;
 169        unsigned int chan = CR_CHAN(insn->chanspec);
 170        unsigned int val = s->readback[chan];
 171        unsigned int gpioc;
 172        int i;
 173
 174        /* Enable instantaneous update of converters outputs + Enable DACs */
 175        gpioc = ioread32(devpriv->gpioc_reg);
 176        iowrite32((gpioc & ~MF6X4_GPIOC_LDAC) | MF6X4_GPIOC_DACEN,
 177                  devpriv->gpioc_reg);
 178
 179        for (i = 0; i < insn->n; i++) {
 180                val = data[i];
 181                iowrite16(val, dev->mmio + MF6X4_DAC_REG(chan));
 182        }
 183        s->readback[chan] = val;
 184
 185        return insn->n;
 186}
 187
 188static int mf6x4_auto_attach(struct comedi_device *dev, unsigned long context)
 189{
 190        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 191        const struct mf6x4_board *board = NULL;
 192        struct mf6x4_private *devpriv;
 193        struct comedi_subdevice *s;
 194        int ret;
 195
 196        if (context < ARRAY_SIZE(mf6x4_boards))
 197                board = &mf6x4_boards[context];
 198        else
 199                return -ENODEV;
 200
 201        dev->board_ptr = board;
 202        dev->board_name = board->name;
 203
 204        ret = comedi_pci_enable(dev);
 205        if (ret)
 206                return ret;
 207
 208        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 209        if (!devpriv)
 210                return -ENOMEM;
 211
 212        devpriv->bar0_mem = pci_ioremap_bar(pcidev, board->bar_nums[0]);
 213        if (!devpriv->bar0_mem)
 214                return -ENODEV;
 215
 216        dev->mmio = pci_ioremap_bar(pcidev, board->bar_nums[1]);
 217        if (!dev->mmio)
 218                return -ENODEV;
 219
 220        devpriv->bar2_mem = pci_ioremap_bar(pcidev, board->bar_nums[2]);
 221        if (!devpriv->bar2_mem)
 222                return -ENODEV;
 223
 224        if (board == &mf6x4_boards[BOARD_MF634])
 225                devpriv->gpioc_reg = devpriv->bar2_mem + MF634_GPIOC_REG;
 226        else
 227                devpriv->gpioc_reg = devpriv->bar0_mem + MF624_GPIOC_REG;
 228
 229        ret = comedi_alloc_subdevices(dev, 4);
 230        if (ret)
 231                return ret;
 232
 233        /* Analog Input subdevice */
 234        s = &dev->subdevices[0];
 235        s->type         = COMEDI_SUBD_AI;
 236        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 237        s->n_chan       = 8;
 238        s->maxdata      = 0x3fff;
 239        s->range_table  = &range_bipolar10;
 240        s->insn_read    = mf6x4_ai_insn_read;
 241
 242        /* Analog Output subdevice */
 243        s = &dev->subdevices[1];
 244        s->type         = COMEDI_SUBD_AO;
 245        s->subdev_flags = SDF_WRITABLE;
 246        s->n_chan       = 8;
 247        s->maxdata      = 0x3fff;
 248        s->range_table  = &range_bipolar10;
 249        s->insn_write   = mf6x4_ao_insn_write;
 250
 251        ret = comedi_alloc_subdev_readback(s);
 252        if (ret)
 253                return ret;
 254
 255        /* Digital Input subdevice */
 256        s = &dev->subdevices[2];
 257        s->type         = COMEDI_SUBD_DI;
 258        s->subdev_flags = SDF_READABLE;
 259        s->n_chan       = 8;
 260        s->maxdata      = 1;
 261        s->range_table  = &range_digital;
 262        s->insn_bits    = mf6x4_di_insn_bits;
 263
 264        /* Digital Output subdevice */
 265        s = &dev->subdevices[3];
 266        s->type         = COMEDI_SUBD_DO;
 267        s->subdev_flags = SDF_WRITABLE;
 268        s->n_chan       = 8;
 269        s->maxdata      = 1;
 270        s->range_table  = &range_digital;
 271        s->insn_bits    = mf6x4_do_insn_bits;
 272
 273        return 0;
 274}
 275
 276static void mf6x4_detach(struct comedi_device *dev)
 277{
 278        struct mf6x4_private *devpriv = dev->private;
 279
 280        if (devpriv) {
 281                if (devpriv->bar0_mem)
 282                        iounmap(devpriv->bar0_mem);
 283                if (devpriv->bar2_mem)
 284                        iounmap(devpriv->bar2_mem);
 285        }
 286        comedi_pci_detach(dev);
 287}
 288
 289static struct comedi_driver mf6x4_driver = {
 290        .driver_name    = "mf6x4",
 291        .module         = THIS_MODULE,
 292        .auto_attach    = mf6x4_auto_attach,
 293        .detach         = mf6x4_detach,
 294};
 295
 296static int mf6x4_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 297{
 298        return comedi_pci_auto_config(dev, &mf6x4_driver, id->driver_data);
 299}
 300
 301static const struct pci_device_id mf6x4_pci_table[] = {
 302        { PCI_VDEVICE(HUMUSOFT, 0x0634), BOARD_MF634 },
 303        { PCI_VDEVICE(HUMUSOFT, 0x0624), BOARD_MF624 },
 304        { 0 }
 305};
 306MODULE_DEVICE_TABLE(pci, mf6x4_pci_table);
 307
 308static struct pci_driver mf6x4_pci_driver = {
 309        .name           = "mf6x4",
 310        .id_table       = mf6x4_pci_table,
 311        .probe          = mf6x4_pci_probe,
 312        .remove         = comedi_pci_auto_unconfig,
 313};
 314
 315module_comedi_pci_driver(mf6x4_driver, mf6x4_pci_driver);
 316
 317MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
 318MODULE_DESCRIPTION("Comedi MF634 and MF624 DAQ cards driver");
 319MODULE_LICENSE("GPL");
 320