// SPDX-License-Identifier: GPL-2.0+ /* * Comedi driver for NI AT-MIO E series cards * * COMEDI - Linux Control and Measurement Device Interface * Copyright (C) 1997-2001 David A. Schleef */ /* * Driver: ni_atmio * Description: National Instruments AT-MIO-E series * Author: ds * Devices: [National Instruments] AT-MIO-16E-1 (ni_atmio), * AT-MIO-16E-2, AT-MIO-16E-10, AT-MIO-16DE-10, AT-MIO-64E-3, * AT-MIO-16XE-50, AT-MIO-16XE-10, AT-AI-16XE-10 * Status: works * Updated: Thu May 1 20:03:02 CDT 2003 * * The driver has 2.6 kernel isapnp support, and will automatically probe for * a supported board if the I/O base is left unspecified with comedi_config. * However, many of the isapnp id numbers are unknown. If your board is not * recognized, please send the output of 'cat /proc/isapnp' (you may need to * modprobe the isa-pnp module for /proc/isapnp to exist) so the id numbers * for your board can be added to the driver. * * Otherwise, you can use the isapnptools package to configure your board. * Use isapnp to configure the I/O base and IRQ for the board, and then pass * the same values as parameters in comedi_config. A sample isapnp.conf file * is included in the etc/ directory of Comedilib. * * Comedilib includes a utility to autocalibrate these boards. The boards * seem to boot into a state where the all calibration DACs are at one * extreme of their range, thus the default calibration is terrible. * Calibration at boot is strongly encouraged. * * To use the extended digital I/O on some of the boards, enable the * 8255 driver when configuring the Comedi source tree. * * External triggering is supported for some events. The channel index * (scan_begin_arg, etc.) maps to PFI0 - PFI9. * * Some of the more esoteric triggering possibilities of these boards are * not supported. */ /* * The real guts of the driver is in ni_mio_common.c, which is included * both here and in ni_pcimio.c * * Interrupt support added by Truxton Fulton * * References for specifications: * 340747b.pdf Register Level Programmer Manual (obsolete) * 340747c.pdf Register Level Programmer Manual (new) * DAQ-STC reference manual * * Other possibly relevant info: * 320517c.pdf User manual (obsolete) * 320517f.pdf User manual (new) * 320889a.pdf delete * 320906c.pdf maximum signal ratings * 321066a.pdf about 16x * 321791a.pdf discontinuation of at-mio-16e-10 rev. c * 321808a.pdf about at-mio-16e-10 rev P * 321837a.pdf discontinuation of at-mio-16de-10 rev d * 321838a.pdf about at-mio-16de-10 rev N * * ISSUES: * - need to deal with external reference for DAC, and other DAC * properties in board properties * - deal with at-mio-16de-10 revision D to N changes, etc. */ #include #include #include "../comedidev.h" #include #include "ni_stc.h" #include "8255.h" /* AT specific setup */ static const struct ni_board_struct ni_boards[] = { { .name = "at-mio-16e-1", .device_id = 44, .isapnp_id = 0x0000, /* XXX unknown */ .n_adchan = 16, .ai_maxdata = 0x0fff, .ai_fifo_depth = 8192, .gainlkup = ai_gain_16, .ai_speed = 800, .n_aochan = 2, .ao_maxdata = 0x0fff, .ao_fifo_depth = 2048, .ao_range_table = &range_ni_E_ao_ext, .ao_speed = 1000, .caldac = { mb88341 }, }, { .name = "at-mio-16e-2", .device_id = 25, .isapnp_id = 0x1900, .n_adchan = 16, .ai_maxdata = 0x0fff, .ai_fifo_depth = 2048, .gainlkup = ai_gain_16, .ai_speed = 2000, .n_aochan = 2, .ao_maxdata = 0x0fff, .ao_fifo_depth = 2048, .ao_range_table = &range_ni_E_ao_ext, .ao_speed = 1000, .caldac = { mb88341 }, }, { .name = "at-mio-16e-10", .device_id = 36, .isapnp_id = 0x2400, .n_adchan = 16, .ai_maxdata = 0x0fff, .ai_fifo_depth = 512, .gainlkup = ai_gain_16, .ai_speed = 10000, .n_aochan = 2, .ao_maxdata = 0x0fff, .ao_range_table = &range_ni_E_ao_ext, .ao_speed = 10000, .caldac = { ad8804_debug }, }, { .name = "at-mio-16de-10", .device_id = 37, .isapnp_id = 0x2500, .n_adchan = 16, .ai_maxdata = 0x0fff, .ai_fifo_depth = 512, .gainlkup = ai_gain_16, .ai_speed = 10000, .n_aochan = 2, .ao_maxdata = 0x0fff, .ao_range_table = &range_ni_E_ao_ext, .ao_speed = 10000, .caldac = { ad8804_debug }, .has_8255 = 1, }, { .name = "at-mio-64e-3", .device_id = 38, .isapnp_id = 0x2600, .n_adchan = 64, .ai_maxdata = 0x0fff, .ai_fifo_depth = 2048, .gainlkup = ai_gain_16, .ai_speed = 2000, .n_aochan = 2, .ao_maxdata = 0x0fff, .ao_fifo_depth = 2048, .ao_range_table = &range_ni_E_ao_ext, .ao_speed = 1000, .caldac = { ad8804_debug }, }, { .name = "at-mio-16xe-50", .device_id = 39, .isapnp_id = 0x2700, .n_adchan = 16, .ai_maxdata = 0xffff, .ai_fifo_depth = 512, .alwaysdither = 1, .gainlkup = ai_gain_8, .ai_speed = 50000, .n_aochan = 2, .ao_maxdata = 0x0fff, .ao_range_table = &range_bipolar10, .ao_speed = 50000, .caldac = { dac8800, dac8043 }, }, { .name = "at-mio-16xe-10", .device_id = 50, .isapnp_id = 0x0000, /* XXX unknown */ .n_adchan = 16, .ai_maxdata = 0xffff, .ai_fifo_depth = 512, .alwaysdither = 1, .gainlkup = ai_gain_14, .ai_speed = 10000, .n_aochan = 2, .ao_maxdata = 0xffff, .ao_fifo_depth = 2048, .ao_range_table = &range_ni_E_ao_ext, .ao_speed = 1000, .caldac = { dac8800, dac8043, ad8522 }, }, { .name = "at-ai-16xe-10", .device_id = 51, .isapnp_id = 0x0000, /* XXX unknown */ .n_adchan = 16, .ai_maxdata = 0xffff, .ai_fifo_depth = 512, .alwaysdither = 1, /* unknown */ .gainlkup = ai_gain_14, .ai_speed = 10000, .caldac = { dac8800, dac8043, ad8522 }, }, }; static const int ni_irqpin[] = { -1, -1, -1, 0, 1, 2, -1, 3, -1, -1, 4, 5, 6, -1, -1, 7 }; #include "ni_mio_common.c" static const struct pnp_device_id device_ids[] = { {.id = "NIC1900", .driver_data = 0}, {.id = "NIC2400", .driver_data = 0}, {.id = "NIC2500", .driver_data = 0}, {.id = "NIC2600", .driver_data = 0}, {.id = "NIC2700", .driver_data = 0}, {.id = ""} }; MODULE_DEVICE_TABLE(pnp, device_ids); static int ni_isapnp_find_board(struct pnp_dev **dev) { struct pnp_dev *isapnp_dev = NULL; int i; for (i = 0; i < ARRAY_SIZE(ni_boards); i++) { isapnp_dev = pnp_find_dev(NULL, ISAPNP_VENDOR('N', 'I', 'C'), ISAPNP_FUNCTION(ni_boards[i].isapnp_id), NULL); if (!isapnp_dev || !isapnp_dev->card) continue; if (pnp_device_attach(isapnp_dev) < 0) continue; if (pnp_activate_dev(isapnp_dev) < 0) { pnp_device_detach(isapnp_dev); return -EAGAIN; } if (!pnp_port_valid(isapnp_dev, 0) || !pnp_irq_valid(isapnp_dev, 0)) { pnp_device_detach(isapnp_dev); return -ENOMEM; } break; } if (i == ARRAY_SIZE(ni_boards)) return -ENODEV; *dev = isapnp_dev; return 0; } static const struct ni_board_struct *ni_atmio_probe(struct comedi_device *dev) { int device_id = ni_read_eeprom(dev, 511); int i; for (i = 0; i < ARRAY_SIZE(ni_boards); i++) { const struct ni_board_struct *board = &ni_boards[i]; if (board->device_id == device_id) return board; } if (device_id == 255) dev_err(dev->class_dev, "can't find board\n"); else if (device_id == 0) dev_err(dev->class_dev, "EEPROM read error (?) or device not found\n"); else dev_err(dev->class_dev, "unknown device ID %d -- contact author\n", device_id); return NULL; } static int ni_atmio_attach(struct comedi_device *dev, struct comedi_devconfig *it) { const struct ni_board_struct *board; struct pnp_dev *isapnp_dev; int ret; unsigned long iobase; unsigned int irq; ret = ni_alloc_private(dev); if (ret) return ret; iobase = it->options[0]; irq = it->options[1]; isapnp_dev = NULL; if (iobase == 0) { ret = ni_isapnp_find_board(&isapnp_dev); if (ret < 0) return ret; iobase = pnp_port_start(isapnp_dev, 0); irq = pnp_irq(isapnp_dev, 0); comedi_set_hw_dev(dev, &isapnp_dev->dev); } ret = comedi_request_region(dev, iobase, 0x20); if (ret) return ret; board = ni_atmio_probe(dev); if (!board) return -ENODEV; dev->board_ptr = board; dev->board_name = board->name; /* irq stuff */ if (irq != 0) { if (irq > 15 || ni_irqpin[irq] == -1) return -EINVAL; ret = request_irq(irq, ni_E_interrupt, 0, dev->board_name, dev); if (ret < 0) return -EINVAL; dev->irq = irq; } /* generic E series stuff in ni_mio_common.c */ ret = ni_E_init(dev, ni_irqpin[dev->irq], 0); if (ret < 0) return ret; return 0; } static void ni_atmio_detach(struct comedi_device *dev) { struct pnp_dev *isapnp_dev; mio_common_detach(dev); comedi_legacy_detach(dev); isapnp_dev = dev->hw_dev ? to_pnp_dev(dev->hw_dev) : NULL; if (isapnp_dev) pnp_device_detach(isapnp_dev); } static struct comedi_driver ni_atmio_driver = { .driver_name = "ni_atmio", .module = THIS_MODULE, .attach = ni_atmio_attach, .detach = ni_atmio_detach, }; module_comedi_driver(ni_atmio_driver); MODULE_AUTHOR("Comedi http://www.comedi.org"); MODULE_DESCRIPTION("Comedi low-level driver"); MODULE_LICENSE("GPL");