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