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 * You should have received a copy of the GNU General Public License
  24 * along with this program; if not, write to the Free Software
  25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  26 */
  27
  28/*
  29Driver: adl_pci7x3x
  30Description: 32/64-Channel Isolated Digital I/O Boards
  31Devices: (ADLink) PCI-7230 [adl_pci7230] - 16 input / 16 output
  32         (ADLink) PCI-7233 [adl_pci7233] - 32 input
  33         (ADLink) PCI-7234 [adl_pci7234] - 32 output
  34         (ADLink) PCI-7432 [adl_pci7432] - 32 input / 32 output
  35         (ADLink) PCI-7433 [adl_pci7433] - 64 input
  36         (ADLink) PCI-7434 [adl_pci7434] - 64 output
  37Author: H Hartley Sweeten <hsweeten@visionengravers.com>
  38Updated: Thu, 02 Aug 2012 14:27:46 -0700
  39Status: untested
  40
  41The PCI-7230, PCI-7432 and PCI-7433 boards also support external
  42interrupt signals on digital input channels 0 and 1. The PCI-7233
  43has dual-interrupt sources for change-of-state (COS) on any 16
  44digital input channels of LSB and for COS on any 16 digital input
  45lines of MSB. Interrupts are not currently supported by this
  46driver.
  47
  48Configuration Options: not applicable, uses comedi PCI auto config
  49*/
  50
  51#include <linux/pci.h>
  52
  53#include "../comedidev.h"
  54
  55/*
  56 * Register I/O map (32-bit access only)
  57 */
  58#define PCI7X3X_DIO_REG         0x00
  59#define PCI743X_DIO_REG         0x04
  60
  61enum apci1516_boardid {
  62        BOARD_PCI7230,
  63        BOARD_PCI7233,
  64        BOARD_PCI7234,
  65        BOARD_PCI7432,
  66        BOARD_PCI7433,
  67        BOARD_PCI7434,
  68};
  69
  70struct adl_pci7x3x_boardinfo {
  71        const char *name;
  72        int nsubdevs;
  73        int di_nchan;
  74        int do_nchan;
  75};
  76
  77static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
  78        [BOARD_PCI7230] = {
  79                .name           = "adl_pci7230",
  80                .nsubdevs       = 2,
  81                .di_nchan       = 16,
  82                .do_nchan       = 16,
  83        },
  84        [BOARD_PCI7233] = {
  85                .name           = "adl_pci7233",
  86                .nsubdevs       = 1,
  87                .di_nchan       = 32,
  88        },
  89        [BOARD_PCI7234] = {
  90                .name           = "adl_pci7234",
  91                .nsubdevs       = 1,
  92                .do_nchan       = 32,
  93        },
  94        [BOARD_PCI7432] = {
  95                .name           = "adl_pci7432",
  96                .nsubdevs       = 2,
  97                .di_nchan       = 32,
  98                .do_nchan       = 32,
  99        },
 100        [BOARD_PCI7433] = {
 101                .name           = "adl_pci7433",
 102                .nsubdevs       = 2,
 103                .di_nchan       = 64,
 104        },
 105        [BOARD_PCI7434] = {
 106                .name           = "adl_pci7434",
 107                .nsubdevs       = 2,
 108                .do_nchan       = 64,
 109        }
 110};
 111
 112static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev,
 113                                    struct comedi_subdevice *s,
 114                                    struct comedi_insn *insn,
 115                                    unsigned int *data)
 116{
 117        unsigned long reg = (unsigned long)s->private;
 118        unsigned int mask = data[0];
 119        unsigned int bits = data[1];
 120
 121        if (mask) {
 122                s->state &= ~mask;
 123                s->state |= (bits & mask);
 124
 125                outl(s->state, dev->iobase + reg);
 126        }
 127
 128        /*
 129         * NOTE: The output register is not readable.
 130         * This returned state will not be correct until all the
 131         * outputs have been updated.
 132         */
 133        data[1] = s->state;
 134
 135        return insn->n;
 136}
 137
 138static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
 139                                    struct comedi_subdevice *s,
 140                                    struct comedi_insn *insn,
 141                                    unsigned int *data)
 142{
 143        unsigned long reg = (unsigned long)s->private;
 144
 145        data[1] = inl(dev->iobase + reg);
 146
 147        return insn->n;
 148}
 149
 150static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
 151                                   unsigned long context)
 152{
 153        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 154        const struct adl_pci7x3x_boardinfo *board = NULL;
 155        struct comedi_subdevice *s;
 156        int subdev;
 157        int nchan;
 158        int ret;
 159
 160        if (context < ARRAY_SIZE(adl_pci7x3x_boards))
 161                board = &adl_pci7x3x_boards[context];
 162        if (!board)
 163                return -ENODEV;
 164        dev->board_ptr = board;
 165        dev->board_name = board->name;
 166
 167        ret = comedi_pci_enable(dev);
 168        if (ret)
 169                return ret;
 170        dev->iobase = pci_resource_start(pcidev, 2);
 171
 172        /*
 173         * One or two subdevices are setup by this driver depending on
 174         * the number of digital inputs and/or outputs provided by the
 175         * board. Each subdevice has a maximum of 32 channels.
 176         *
 177         *      PCI-7230 - 2 subdevices: 0 - 16 input, 1 - 16 output
 178         *      PCI-7233 - 1 subdevice: 0 - 32 input
 179         *      PCI-7234 - 1 subdevice: 0 - 32 output
 180         *      PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output
 181         *      PCI-7433 - 2 subdevices: 0 - 32 input, 1 - 32 input
 182         *      PCI-7434 - 2 subdevices: 0 - 32 output, 1 - 32 output
 183         */
 184        ret = comedi_alloc_subdevices(dev, board->nsubdevs);
 185        if (ret)
 186                return ret;
 187
 188        subdev = 0;
 189
 190        if (board->di_nchan) {
 191                nchan = min(board->di_nchan, 32);
 192
 193                s = &dev->subdevices[subdev];
 194                /* Isolated digital inputs 0 to 15/31 */
 195                s->type         = COMEDI_SUBD_DI;
 196                s->subdev_flags = SDF_READABLE;
 197                s->n_chan       = nchan;
 198                s->maxdata      = 1;
 199                s->insn_bits    = adl_pci7x3x_di_insn_bits;
 200                s->range_table  = &range_digital;
 201
 202                s->private      = (void *)PCI7X3X_DIO_REG;
 203
 204                subdev++;
 205
 206                nchan = board->di_nchan - nchan;
 207                if (nchan) {
 208                        s = &dev->subdevices[subdev];
 209                        /* Isolated digital inputs 32 to 63 */
 210                        s->type         = COMEDI_SUBD_DI;
 211                        s->subdev_flags = SDF_READABLE;
 212                        s->n_chan       = nchan;
 213                        s->maxdata      = 1;
 214                        s->insn_bits    = adl_pci7x3x_di_insn_bits;
 215                        s->range_table  = &range_digital;
 216
 217                        s->private      = (void *)PCI743X_DIO_REG;
 218
 219                        subdev++;
 220                }
 221        }
 222
 223        if (board->do_nchan) {
 224                nchan = min(board->do_nchan, 32);
 225
 226                s = &dev->subdevices[subdev];
 227                /* Isolated digital outputs 0 to 15/31 */
 228                s->type         = COMEDI_SUBD_DO;
 229                s->subdev_flags = SDF_WRITABLE;
 230                s->n_chan       = nchan;
 231                s->maxdata      = 1;
 232                s->insn_bits    = adl_pci7x3x_do_insn_bits;
 233                s->range_table  = &range_digital;
 234
 235                s->private      = (void *)PCI7X3X_DIO_REG;
 236
 237                subdev++;
 238
 239                nchan = board->do_nchan - nchan;
 240                if (nchan) {
 241                        s = &dev->subdevices[subdev];
 242                        /* Isolated digital outputs 32 to 63 */
 243                        s->type         = COMEDI_SUBD_DO;
 244                        s->subdev_flags = SDF_WRITABLE;
 245                        s->n_chan       = nchan;
 246                        s->maxdata      = 1;
 247                        s->insn_bits    = adl_pci7x3x_do_insn_bits;
 248                        s->range_table  = &range_digital;
 249
 250                        s->private      = (void *)PCI743X_DIO_REG;
 251
 252                        subdev++;
 253                }
 254        }
 255
 256        dev_info(dev->class_dev, "%s attached (%d inputs/%d outputs)\n",
 257                dev->board_name, board->di_nchan, board->do_nchan);
 258
 259        return 0;
 260}
 261
 262static struct comedi_driver adl_pci7x3x_driver = {
 263        .driver_name    = "adl_pci7x3x",
 264        .module         = THIS_MODULE,
 265        .auto_attach    = adl_pci7x3x_auto_attach,
 266        .detach         = comedi_pci_disable,
 267};
 268
 269static int adl_pci7x3x_pci_probe(struct pci_dev *dev,
 270                                 const struct pci_device_id *id)
 271{
 272        return comedi_pci_auto_config(dev, &adl_pci7x3x_driver,
 273                                      id->driver_data);
 274}
 275
 276static DEFINE_PCI_DEVICE_TABLE(adl_pci7x3x_pci_table) = {
 277        { PCI_VDEVICE(ADLINK, 0x7230), BOARD_PCI7230 },
 278        { PCI_VDEVICE(ADLINK, 0x7233), BOARD_PCI7233 },
 279        { PCI_VDEVICE(ADLINK, 0x7234), BOARD_PCI7234 },
 280        { PCI_VDEVICE(ADLINK, 0x7432), BOARD_PCI7432 },
 281        { PCI_VDEVICE(ADLINK, 0x7433), BOARD_PCI7433 },
 282        { PCI_VDEVICE(ADLINK, 0x7434), BOARD_PCI7434 },
 283        { 0 }
 284};
 285MODULE_DEVICE_TABLE(pci, adl_pci7x3x_pci_table);
 286
 287static struct pci_driver adl_pci7x3x_pci_driver = {
 288        .name           = "adl_pci7x3x",
 289        .id_table       = adl_pci7x3x_pci_table,
 290        .probe          = adl_pci7x3x_pci_probe,
 291        .remove         = comedi_pci_auto_unconfig,
 292};
 293module_comedi_pci_driver(adl_pci7x3x_driver, adl_pci7x3x_pci_driver);
 294
 295MODULE_DESCRIPTION("ADLINK PCI-723x/743x Isolated Digital I/O boards");
 296MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
 297MODULE_LICENSE("GPL");
 298