linux/drivers/staging/comedi/drivers/ni_atmio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Comedi driver for NI AT-MIO E series cards
   4 *
   5 * COMEDI - Linux Control and Measurement Device Interface
   6 * Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>
   7 */
   8
   9/*
  10 * Driver: ni_atmio
  11 * Description: National Instruments AT-MIO-E series
  12 * Author: ds
  13 * Devices: [National Instruments] AT-MIO-16E-1 (ni_atmio),
  14 *   AT-MIO-16E-2, AT-MIO-16E-10, AT-MIO-16DE-10, AT-MIO-64E-3,
  15 *   AT-MIO-16XE-50, AT-MIO-16XE-10, AT-AI-16XE-10
  16 * Status: works
  17 * Updated: Thu May  1 20:03:02 CDT 2003
  18 *
  19 * The driver has 2.6 kernel isapnp support, and will automatically probe for
  20 * a supported board if the I/O base is left unspecified with comedi_config.
  21 * However, many of the isapnp id numbers are unknown. If your board is not
  22 * recognized, please send the output of 'cat /proc/isapnp' (you may need to
  23 * modprobe the isa-pnp module for /proc/isapnp to exist) so the id numbers
  24 * for your board can be added to the driver.
  25 *
  26 * Otherwise, you can use the isapnptools package to configure your board.
  27 * Use isapnp to configure the I/O base and IRQ for the board, and then pass
  28 * the same values as parameters in comedi_config. A sample isapnp.conf file
  29 * is included in the etc/ directory of Comedilib.
  30 *
  31 * Comedilib includes a utility to autocalibrate these boards. The boards
  32 * seem to boot into a state where the all calibration DACs are at one
  33 * extreme of their range, thus the default calibration is terrible.
  34 * Calibration at boot is strongly encouraged.
  35 *
  36 * To use the extended digital I/O on some of the boards, enable the
  37 * 8255 driver when configuring the Comedi source tree.
  38 *
  39 * External triggering is supported for some events. The channel index
  40 * (scan_begin_arg, etc.) maps to PFI0 - PFI9.
  41 *
  42 * Some of the more esoteric triggering possibilities of these boards are
  43 * not supported.
  44 */
  45
  46/*
  47 * The real guts of the driver is in ni_mio_common.c, which is included
  48 * both here and in ni_pcimio.c
  49 *
  50 * Interrupt support added by Truxton Fulton <trux@truxton.com>
  51 *
  52 * References for specifications:
  53 *      340747b.pdf  Register Level Programmer Manual (obsolete)
  54 *      340747c.pdf  Register Level Programmer Manual (new)
  55 *                   DAQ-STC reference manual
  56 *
  57 * Other possibly relevant info:
  58 *      320517c.pdf  User manual (obsolete)
  59 *      320517f.pdf  User manual (new)
  60 *      320889a.pdf  delete
  61 *      320906c.pdf  maximum signal ratings
  62 *      321066a.pdf  about 16x
  63 *      321791a.pdf  discontinuation of at-mio-16e-10 rev. c
  64 *      321808a.pdf  about at-mio-16e-10 rev P
  65 *      321837a.pdf  discontinuation of at-mio-16de-10 rev d
  66 *      321838a.pdf  about at-mio-16de-10 rev N
  67 *
  68 * ISSUES:
  69 * - need to deal with external reference for DAC, and other DAC
  70 *   properties in board properties
  71 * - deal with at-mio-16de-10 revision D to N changes, etc.
  72 */
  73
  74#include <linux/module.h>
  75#include <linux/interrupt.h>
  76#include "../comedidev.h"
  77
  78#include <linux/isapnp.h>
  79
  80#include "ni_stc.h"
  81#include "8255.h"
  82
  83/* AT specific setup */
  84static const struct ni_board_struct ni_boards[] = {
  85        {
  86                .name           = "at-mio-16e-1",
  87                .device_id      = 44,
  88                .isapnp_id      = 0x0000,       /* XXX unknown */
  89                .n_adchan       = 16,
  90                .ai_maxdata     = 0x0fff,
  91                .ai_fifo_depth  = 8192,
  92                .gainlkup       = ai_gain_16,
  93                .ai_speed       = 800,
  94                .n_aochan       = 2,
  95                .ao_maxdata     = 0x0fff,
  96                .ao_fifo_depth  = 2048,
  97                .ao_range_table = &range_ni_E_ao_ext,
  98                .ao_speed       = 1000,
  99                .caldac         = { mb88341 },
 100        }, {
 101                .name           = "at-mio-16e-2",
 102                .device_id      = 25,
 103                .isapnp_id      = 0x1900,
 104                .n_adchan       = 16,
 105                .ai_maxdata     = 0x0fff,
 106                .ai_fifo_depth  = 2048,
 107                .gainlkup       = ai_gain_16,
 108                .ai_speed       = 2000,
 109                .n_aochan       = 2,
 110                .ao_maxdata     = 0x0fff,
 111                .ao_fifo_depth  = 2048,
 112                .ao_range_table = &range_ni_E_ao_ext,
 113                .ao_speed       = 1000,
 114                .caldac         = { mb88341 },
 115        }, {
 116                .name           = "at-mio-16e-10",
 117                .device_id      = 36,
 118                .isapnp_id      = 0x2400,
 119                .n_adchan       = 16,
 120                .ai_maxdata     = 0x0fff,
 121                .ai_fifo_depth  = 512,
 122                .gainlkup       = ai_gain_16,
 123                .ai_speed       = 10000,
 124                .n_aochan       = 2,
 125                .ao_maxdata     = 0x0fff,
 126                .ao_range_table = &range_ni_E_ao_ext,
 127                .ao_speed       = 10000,
 128                .caldac         = { ad8804_debug },
 129        }, {
 130                .name           = "at-mio-16de-10",
 131                .device_id      = 37,
 132                .isapnp_id      = 0x2500,
 133                .n_adchan       = 16,
 134                .ai_maxdata     = 0x0fff,
 135                .ai_fifo_depth  = 512,
 136                .gainlkup       = ai_gain_16,
 137                .ai_speed       = 10000,
 138                .n_aochan       = 2,
 139                .ao_maxdata     = 0x0fff,
 140                .ao_range_table = &range_ni_E_ao_ext,
 141                .ao_speed       = 10000,
 142                .caldac         = { ad8804_debug },
 143                .has_8255       = 1,
 144        }, {
 145                .name           = "at-mio-64e-3",
 146                .device_id      = 38,
 147                .isapnp_id      = 0x2600,
 148                .n_adchan       = 64,
 149                .ai_maxdata     = 0x0fff,
 150                .ai_fifo_depth  = 2048,
 151                .gainlkup       = ai_gain_16,
 152                .ai_speed       = 2000,
 153                .n_aochan       = 2,
 154                .ao_maxdata     = 0x0fff,
 155                .ao_fifo_depth  = 2048,
 156                .ao_range_table = &range_ni_E_ao_ext,
 157                .ao_speed       = 1000,
 158                .caldac         = { ad8804_debug },
 159        }, {
 160                .name           = "at-mio-16xe-50",
 161                .device_id      = 39,
 162                .isapnp_id      = 0x2700,
 163                .n_adchan       = 16,
 164                .ai_maxdata     = 0xffff,
 165                .ai_fifo_depth  = 512,
 166                .alwaysdither   = 1,
 167                .gainlkup       = ai_gain_8,
 168                .ai_speed       = 50000,
 169                .n_aochan       = 2,
 170                .ao_maxdata     = 0x0fff,
 171                .ao_range_table = &range_bipolar10,
 172                .ao_speed       = 50000,
 173                .caldac         = { dac8800, dac8043 },
 174        }, {
 175                .name           = "at-mio-16xe-10",
 176                .device_id      = 50,
 177                .isapnp_id      = 0x0000,       /* XXX unknown */
 178                .n_adchan       = 16,
 179                .ai_maxdata     = 0xffff,
 180                .ai_fifo_depth  = 512,
 181                .alwaysdither   = 1,
 182                .gainlkup       = ai_gain_14,
 183                .ai_speed       = 10000,
 184                .n_aochan       = 2,
 185                .ao_maxdata     = 0xffff,
 186                .ao_fifo_depth  = 2048,
 187                .ao_range_table = &range_ni_E_ao_ext,
 188                .ao_speed       = 1000,
 189                .caldac         = { dac8800, dac8043, ad8522 },
 190        }, {
 191                .name           = "at-ai-16xe-10",
 192                .device_id      = 51,
 193                .isapnp_id      = 0x0000,       /* XXX unknown */
 194                .n_adchan       = 16,
 195                .ai_maxdata     = 0xffff,
 196                .ai_fifo_depth  = 512,
 197                .alwaysdither   = 1,            /* unknown */
 198                .gainlkup       = ai_gain_14,
 199                .ai_speed       = 10000,
 200                .caldac         = { dac8800, dac8043, ad8522 },
 201        },
 202};
 203
 204static const int ni_irqpin[] = {
 205        -1, -1, -1, 0, 1, 2, -1, 3, -1, -1, 4, 5, 6, -1, -1, 7
 206};
 207
 208#include "ni_mio_common.c"
 209
 210static const struct pnp_device_id device_ids[] = {
 211        {.id = "NIC1900", .driver_data = 0},
 212        {.id = "NIC2400", .driver_data = 0},
 213        {.id = "NIC2500", .driver_data = 0},
 214        {.id = "NIC2600", .driver_data = 0},
 215        {.id = "NIC2700", .driver_data = 0},
 216        {.id = ""}
 217};
 218
 219MODULE_DEVICE_TABLE(pnp, device_ids);
 220
 221static int ni_isapnp_find_board(struct pnp_dev **dev)
 222{
 223        struct pnp_dev *isapnp_dev = NULL;
 224        int i;
 225
 226        for (i = 0; i < ARRAY_SIZE(ni_boards); i++) {
 227                isapnp_dev =
 228                        pnp_find_dev(NULL,
 229                                     ISAPNP_VENDOR('N', 'I', 'C'),
 230                                     ISAPNP_FUNCTION(ni_boards[i].isapnp_id),
 231                                     NULL);
 232
 233                if (!isapnp_dev || !isapnp_dev->card)
 234                        continue;
 235
 236                if (pnp_device_attach(isapnp_dev) < 0)
 237                        continue;
 238
 239                if (pnp_activate_dev(isapnp_dev) < 0) {
 240                        pnp_device_detach(isapnp_dev);
 241                        return -EAGAIN;
 242                }
 243
 244                if (!pnp_port_valid(isapnp_dev, 0) ||
 245                    !pnp_irq_valid(isapnp_dev, 0)) {
 246                        pnp_device_detach(isapnp_dev);
 247                        return -ENOMEM;
 248                }
 249                break;
 250        }
 251        if (i == ARRAY_SIZE(ni_boards))
 252                return -ENODEV;
 253        *dev = isapnp_dev;
 254        return 0;
 255}
 256
 257static const struct ni_board_struct *ni_atmio_probe(struct comedi_device *dev)
 258{
 259        int device_id = ni_read_eeprom(dev, 511);
 260        int i;
 261
 262        for (i = 0; i < ARRAY_SIZE(ni_boards); i++) {
 263                const struct ni_board_struct *board = &ni_boards[i];
 264
 265                if (board->device_id == device_id)
 266                        return board;
 267        }
 268        if (device_id == 255)
 269                dev_err(dev->class_dev, "can't find board\n");
 270        else if (device_id == 0)
 271                dev_err(dev->class_dev,
 272                        "EEPROM read error (?) or device not found\n");
 273        else
 274                dev_err(dev->class_dev,
 275                        "unknown device ID %d -- contact author\n", device_id);
 276
 277        return NULL;
 278}
 279
 280static int ni_atmio_attach(struct comedi_device *dev,
 281                           struct comedi_devconfig *it)
 282{
 283        const struct ni_board_struct *board;
 284        struct pnp_dev *isapnp_dev;
 285        int ret;
 286        unsigned long iobase;
 287        unsigned int irq;
 288
 289        ret = ni_alloc_private(dev);
 290        if (ret)
 291                return ret;
 292
 293        iobase = it->options[0];
 294        irq = it->options[1];
 295        isapnp_dev = NULL;
 296        if (iobase == 0) {
 297                ret = ni_isapnp_find_board(&isapnp_dev);
 298                if (ret < 0)
 299                        return ret;
 300
 301                iobase = pnp_port_start(isapnp_dev, 0);
 302                irq = pnp_irq(isapnp_dev, 0);
 303                comedi_set_hw_dev(dev, &isapnp_dev->dev);
 304        }
 305
 306        ret = comedi_request_region(dev, iobase, 0x20);
 307        if (ret)
 308                return ret;
 309
 310        board = ni_atmio_probe(dev);
 311        if (!board)
 312                return -ENODEV;
 313        dev->board_ptr = board;
 314        dev->board_name = board->name;
 315
 316        /* irq stuff */
 317
 318        if (irq != 0) {
 319                if (irq > 15 || ni_irqpin[irq] == -1)
 320                        return -EINVAL;
 321                ret = request_irq(irq, ni_E_interrupt, 0,
 322                                  dev->board_name, dev);
 323                if (ret < 0)
 324                        return -EINVAL;
 325                dev->irq = irq;
 326        }
 327
 328        /* generic E series stuff in ni_mio_common.c */
 329
 330        ret = ni_E_init(dev, ni_irqpin[dev->irq], 0);
 331        if (ret < 0)
 332                return ret;
 333
 334        return 0;
 335}
 336
 337static void ni_atmio_detach(struct comedi_device *dev)
 338{
 339        struct pnp_dev *isapnp_dev;
 340
 341        mio_common_detach(dev);
 342        comedi_legacy_detach(dev);
 343
 344        isapnp_dev = dev->hw_dev ? to_pnp_dev(dev->hw_dev) : NULL;
 345        if (isapnp_dev)
 346                pnp_device_detach(isapnp_dev);
 347}
 348
 349static struct comedi_driver ni_atmio_driver = {
 350        .driver_name    = "ni_atmio",
 351        .module         = THIS_MODULE,
 352        .attach         = ni_atmio_attach,
 353        .detach         = ni_atmio_detach,
 354};
 355module_comedi_driver(ni_atmio_driver);
 356
 357MODULE_AUTHOR("Comedi http://www.comedi.org");
 358MODULE_DESCRIPTION("Comedi low-level driver");
 359MODULE_LICENSE("GPL");
 360
 361