linux/drivers/staging/comedi/drivers/adl_pci7x3x.c
<<
>>
Prefs
   1/*
   2 * COMEDI driver for the ADLINK PCI-723x/743x series boards.
   3 * Copyright (C) 2012 H Hartley Sweeten <hsweeten@visionengravers.com>
   4 *
   5 * Based on the adl_pci7230 driver written by:
   6 *      David Fernandez <dfcastelao@gmail.com>
   7 * and the adl_pci7432 driver written by:
   8 *      Michel Lachaine <mike@mikelachaine.ca>
   9 *
  10 * COMEDI - Linux Control and Measurement Device Interface
  11 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
  12 *
  13 * This program is free software; you can redistribute it and/or modify
  14 * it under the terms of the GNU General Public License as published by
  15 * the Free Software Foundation; either version 2 of the License, or
  16 * (at your option) any later version.
  17 *
  18 * This program is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 * GNU General Public License for more details.
  22 */
  23
  24/*
  25 * Driver: adl_pci7x3x
  26 * Description: 32/64-Channel Isolated Digital I/O Boards
  27 * Devices: [ADLink] PCI-7230 (adl_pci7230), PCI-7233 (adl_pci7233),
  28 *   PCI-7234 (adl_pci7234), PCI-7432 (adl_pci7432), PCI-7433 (adl_pci7433),
  29 *   PCI-7434 (adl_pci7434)
  30 * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
  31 * Updated: Thu, 02 Aug 2012 14:27:46 -0700
  32 * Status: untested
  33 *
  34 * One or two subdevices are setup by this driver depending on
  35 * the number of digital inputs and/or outputs provided by the
  36 * board. Each subdevice has a maximum of 32 channels.
  37 *
  38 *      PCI-7230 - 2 subdevices: 0 - 16 input, 1 - 16 output
  39 *      PCI-7233 - 1 subdevice: 0 - 32 input
  40 *      PCI-7234 - 1 subdevice: 0 - 32 output
  41 *      PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output
  42 *      PCI-7433 - 2 subdevices: 0 - 32 input, 1 - 32 input
  43 *      PCI-7434 - 2 subdevices: 0 - 32 output, 1 - 32 output
  44 *
  45 * The PCI-7230, PCI-7432 and PCI-7433 boards also support external
  46 * interrupt signals on digital input channels 0 and 1. The PCI-7233
  47 * has dual-interrupt sources for change-of-state (COS) on any 16
  48 * digital input channels of LSB and for COS on any 16 digital input
  49 * lines of MSB. Interrupts are not currently supported by this
  50 * driver.
  51 *
  52 * Configuration Options: not applicable, uses comedi PCI auto config
  53 */
  54
  55#include <linux/module.h>
  56
  57#include "../comedi_pci.h"
  58
  59/*
  60 * Register I/O map (32-bit access only)
  61 */
  62#define PCI7X3X_DIO_REG         0x00
  63#define PCI743X_DIO_REG         0x04
  64
  65enum apci1516_boardid {
  66        BOARD_PCI7230,
  67        BOARD_PCI7233,
  68        BOARD_PCI7234,
  69        BOARD_PCI7432,
  70        BOARD_PCI7433,
  71        BOARD_PCI7434,
  72};
  73
  74struct adl_pci7x3x_boardinfo {
  75        const char *name;
  76        int nsubdevs;
  77        int di_nchan;
  78        int do_nchan;
  79};
  80
  81static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
  82        [BOARD_PCI7230] = {
  83                .name           = "adl_pci7230",
  84                .nsubdevs       = 2,
  85                .di_nchan       = 16,
  86                .do_nchan       = 16,
  87        },
  88        [BOARD_PCI7233] = {
  89                .name           = "adl_pci7233",
  90                .nsubdevs       = 1,
  91                .di_nchan       = 32,
  92        },
  93        [BOARD_PCI7234] = {
  94                .name           = "adl_pci7234",
  95                .nsubdevs       = 1,
  96                .do_nchan       = 32,
  97        },
  98        [BOARD_PCI7432] = {
  99                .name           = "adl_pci7432",
 100                .nsubdevs       = 2,
 101                .di_nchan       = 32,
 102                .do_nchan       = 32,
 103        },
 104        [BOARD_PCI7433] = {
 105                .name           = "adl_pci7433",
 106                .nsubdevs       = 2,
 107                .di_nchan       = 64,
 108        },
 109        [BOARD_PCI7434] = {
 110                .name           = "adl_pci7434",
 111                .nsubdevs       = 2,
 112                .do_nchan       = 64,
 113        }
 114};
 115
 116static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev,
 117                                    struct comedi_subdevice *s,
 118                                    struct comedi_insn *insn,
 119                                    unsigned int *data)
 120{
 121        unsigned long reg = (unsigned long)s->private;
 122
 123        if (comedi_dio_update_state(s, data)) {
 124                unsigned int val = s->state;
 125
 126                if (s->n_chan == 16) {
 127                        /*
 128                         * It seems the PCI-7230 needs the 16-bit DO state
 129                         * to be shifted left by 16 bits before being written
 130                         * to the 32-bit register.  Set the value in both
 131                         * halves of the register to be sure.
 132                         */
 133                        val |= val << 16;
 134                }
 135                outl(val, dev->iobase + reg);
 136        }
 137
 138        data[1] = s->state;
 139
 140        return insn->n;
 141}
 142
 143static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
 144                                    struct comedi_subdevice *s,
 145                                    struct comedi_insn *insn,
 146                                    unsigned int *data)
 147{
 148        unsigned long reg = (unsigned long)s->private;
 149
 150        data[1] = inl(dev->iobase + reg);
 151
 152        return insn->n;
 153}
 154
 155static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
 156                                   unsigned long context)
 157{
 158        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 159        const struct adl_pci7x3x_boardinfo *board = NULL;
 160        struct comedi_subdevice *s;
 161        int subdev;
 162        int nchan;
 163        int ret;
 164
 165        if (context < ARRAY_SIZE(adl_pci7x3x_boards))
 166                board = &adl_pci7x3x_boards[context];
 167        if (!board)
 168                return -ENODEV;
 169        dev->board_ptr = board;
 170        dev->board_name = board->name;
 171
 172        ret = comedi_pci_enable(dev);
 173        if (ret)
 174                return ret;
 175        dev->iobase = pci_resource_start(pcidev, 2);
 176
 177        ret = comedi_alloc_subdevices(dev, board->nsubdevs);
 178        if (ret)
 179                return ret;
 180
 181        subdev = 0;
 182
 183        if (board->di_nchan) {
 184                nchan = min(board->di_nchan, 32);
 185
 186                s = &dev->subdevices[subdev];
 187                /* Isolated digital inputs 0 to 15/31 */
 188                s->type         = COMEDI_SUBD_DI;
 189                s->subdev_flags = SDF_READABLE;
 190                s->n_chan       = nchan;
 191                s->maxdata      = 1;
 192                s->insn_bits    = adl_pci7x3x_di_insn_bits;
 193                s->range_table  = &range_digital;
 194
 195                s->private      = (void *)PCI7X3X_DIO_REG;
 196
 197                subdev++;
 198
 199                nchan = board->di_nchan - nchan;
 200                if (nchan) {
 201                        s = &dev->subdevices[subdev];
 202                        /* Isolated digital inputs 32 to 63 */
 203                        s->type         = COMEDI_SUBD_DI;
 204                        s->subdev_flags = SDF_READABLE;
 205                        s->n_chan       = nchan;
 206                        s->maxdata      = 1;
 207                        s->insn_bits    = adl_pci7x3x_di_insn_bits;
 208                        s->range_table  = &range_digital;
 209
 210                        s->private      = (void *)PCI743X_DIO_REG;
 211
 212                        subdev++;
 213                }
 214        }
 215
 216        if (board->do_nchan) {
 217                nchan = min(board->do_nchan, 32);
 218
 219                s = &dev->subdevices[subdev];
 220                /* Isolated digital outputs 0 to 15/31 */
 221                s->type         = COMEDI_SUBD_DO;
 222                s->subdev_flags = SDF_WRITABLE;
 223                s->n_chan       = nchan;
 224                s->maxdata      = 1;
 225                s->insn_bits    = adl_pci7x3x_do_insn_bits;
 226                s->range_table  = &range_digital;
 227
 228                s->private      = (void *)PCI7X3X_DIO_REG;
 229
 230                subdev++;
 231
 232                nchan = board->do_nchan - nchan;
 233                if (nchan) {
 234                        s = &dev->subdevices[subdev];
 235                        /* Isolated digital outputs 32 to 63 */
 236                        s->type         = COMEDI_SUBD_DO;
 237                        s->subdev_flags = SDF_WRITABLE;
 238                        s->n_chan       = nchan;
 239                        s->maxdata      = 1;
 240                        s->insn_bits    = adl_pci7x3x_do_insn_bits;
 241                        s->range_table  = &range_digital;
 242
 243                        s->private      = (void *)PCI743X_DIO_REG;
 244
 245                        subdev++;
 246                }
 247        }
 248
 249        return 0;
 250}
 251
 252static struct comedi_driver adl_pci7x3x_driver = {
 253        .driver_name    = "adl_pci7x3x",
 254        .module         = THIS_MODULE,
 255        .auto_attach    = adl_pci7x3x_auto_attach,
 256        .detach         = comedi_pci_detach,
 257};
 258
 259static int adl_pci7x3x_pci_probe(struct pci_dev *dev,
 260                                 const struct pci_device_id *id)
 261{
 262        return comedi_pci_auto_config(dev, &adl_pci7x3x_driver,
 263                                      id->driver_data);
 264}
 265
 266static const struct pci_device_id adl_pci7x3x_pci_table[] = {
 267        { PCI_VDEVICE(ADLINK, 0x7230), BOARD_PCI7230 },
 268        { PCI_VDEVICE(ADLINK, 0x7233), BOARD_PCI7233 },
 269        { PCI_VDEVICE(ADLINK, 0x7234), BOARD_PCI7234 },
 270        { PCI_VDEVICE(ADLINK, 0x7432), BOARD_PCI7432 },
 271        { PCI_VDEVICE(ADLINK, 0x7433), BOARD_PCI7433 },
 272        { PCI_VDEVICE(ADLINK, 0x7434), BOARD_PCI7434 },
 273        { 0 }
 274};
 275MODULE_DEVICE_TABLE(pci, adl_pci7x3x_pci_table);
 276
 277static struct pci_driver adl_pci7x3x_pci_driver = {
 278        .name           = "adl_pci7x3x",
 279        .id_table       = adl_pci7x3x_pci_table,
 280        .probe          = adl_pci7x3x_pci_probe,
 281        .remove         = comedi_pci_auto_unconfig,
 282};
 283module_comedi_pci_driver(adl_pci7x3x_driver, adl_pci7x3x_pci_driver);
 284
 285MODULE_DESCRIPTION("ADLINK PCI-723x/743x Isolated Digital I/O boards");
 286MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
 287MODULE_LICENSE("GPL");
 288