Files
linux/drivers/comedi/drivers/ni_atmio.c

360 lines
9.0 KiB
C
Raw Normal View History

// 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 <ds@schleef.org>
*/
/*
* 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 <trux@truxton.com>
*
* 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 <linux/module.h>
#include <linux/interrupt.h>
#include <linux/comedi/comedidev.h>
#include <linux/isapnp.h>
#include <linux/comedi/comedi_8255.h>
#include "ni_stc.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);
staging: comedi: ni_stc.h: remove NI_PRIVATE_COMMON macro This macro is used to create the private data structure that is used by the ni_atmio, ni_mio_cs, and ni_pcimio drivers. These drivers all include the ni_mio_common.c source to provide most of the driver functionality. The only driver specific information needed to convert the macro into a proper struct definition is the MAX_N_CALDACS define. This define is used to size a couple array members in the struct. The ni_atmio and ni_mio_cs drivers both define MAX_N_CALDACS as 32. The ni_pcimio driver defines it as (16+16+2). The ni_mio_common file only uses this define to sanity check that the struct members are large enough for the number of channels in the calibration subdevice. Move the MAX_N_CALDACS define to ni_stc.h and set it to the largest number of caldacs (34). The ni_atmio and ni_mio_cs drivers also add one additional member to the private data struct before using the NI_PRIVATE_COMMON macro. For the ni_atmio driver, the struct pnp_dev pointer can be saved in the comedi_device as the 'hw_dev'. The (*detach) of this driver can then use to_pnp_dev() to get it back when detaching the pnp device. In the ni_mio_cs driver, the struct pcmia_device pointer is not used so it can simply be removed. The NI_PRIVATE_COMMON macro can then be converted into a proper struct definition. Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com> Reviewed-by: Ian Abbott <abbotti@mev.co.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-05-29 10:56:30 -07:00
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;
}
staging: comedi: cleanup all the comedi_driver 'detach' functions 1. Change the return type from int to void All the detach functions, except for the comedi usb drivers, simply return success (0). Plus, the return code is never checked in the comedi core. The comedi usb drivers do return error codes but the conditions can never happen. The first check is: if (!dev) return -EFAULT; This checks that the passed comedi_device pointer is valid. The detach function itself is called using this pointer so it MUST always be valid or there is a bug in the core: if (dev->driver) dev->driver->detach(dev); And the second check: usb = dev->private; if (!usb) return -EFAULT; The dev->private pointer is setup in the attach function to point to the probed usb device. This value could be NULL if the attach fails. But, since the comedi core is going to unload the driver anyway and does not check for errors there is no gain by returning one. After removing these checks from the comedi usb drivers the detach functions required a bit of cleanup. 2. Remove all the printk noise in the detach functions All of the printk output is really just noise. The user did a rmmod to unload the driver, we really don't need to tell them about it. Also, some of the messages are output using: dev_dbg(dev->hw_dev, ... or dev_info(dev->hw_dev, ... Unfortunately the hw_dev value is only used by drivers that are doing DMA. For most drivers this variable is going to be NULL so the output is not going to work as expected. 3. Refactor a couple static 'free_resource' functions into the detach functions. The 'free_resource' function is only being called by the detach and it makes more sense to just absorb the code. 4. Remove a couple unnecessary braces for single statements. 5. Remove unnecessary comments. Most of the comedi drivers appear to be based on the comedi skel driver and have the comments from that driver included. These comments make sense in the skel driver for reference but they don't need to be in any of the actual drivers. 6. Remove all the extra whitespace. It's not needed to make the functions any more readable. 7. Remove the now unused 'attached_successfully' variable in the cb_pcimdda driver. This variable was only used to conditionally output some driver noise during the detach. Since all the printk's have been removed this variable is no longer necessary. Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com> Cc: Ian Abbott <abbotti@mev.co.uk> Cc: Mori Hess <fmhess@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-05-17 17:11:14 -07:00
static void ni_atmio_detach(struct comedi_device *dev)
{
staging: comedi: ni_stc.h: remove NI_PRIVATE_COMMON macro This macro is used to create the private data structure that is used by the ni_atmio, ni_mio_cs, and ni_pcimio drivers. These drivers all include the ni_mio_common.c source to provide most of the driver functionality. The only driver specific information needed to convert the macro into a proper struct definition is the MAX_N_CALDACS define. This define is used to size a couple array members in the struct. The ni_atmio and ni_mio_cs drivers both define MAX_N_CALDACS as 32. The ni_pcimio driver defines it as (16+16+2). The ni_mio_common file only uses this define to sanity check that the struct members are large enough for the number of channels in the calibration subdevice. Move the MAX_N_CALDACS define to ni_stc.h and set it to the largest number of caldacs (34). The ni_atmio and ni_mio_cs drivers also add one additional member to the private data struct before using the NI_PRIVATE_COMMON macro. For the ni_atmio driver, the struct pnp_dev pointer can be saved in the comedi_device as the 'hw_dev'. The (*detach) of this driver can then use to_pnp_dev() to get it back when detaching the pnp device. In the ni_mio_cs driver, the struct pcmia_device pointer is not used so it can simply be removed. The NI_PRIVATE_COMMON macro can then be converted into a proper struct definition. Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com> Reviewed-by: Ian Abbott <abbotti@mev.co.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-05-29 10:56:30 -07:00
struct pnp_dev *isapnp_dev;
mio_common_detach(dev);
comedi_legacy_detach(dev);
staging: comedi: ni_stc.h: remove NI_PRIVATE_COMMON macro This macro is used to create the private data structure that is used by the ni_atmio, ni_mio_cs, and ni_pcimio drivers. These drivers all include the ni_mio_common.c source to provide most of the driver functionality. The only driver specific information needed to convert the macro into a proper struct definition is the MAX_N_CALDACS define. This define is used to size a couple array members in the struct. The ni_atmio and ni_mio_cs drivers both define MAX_N_CALDACS as 32. The ni_pcimio driver defines it as (16+16+2). The ni_mio_common file only uses this define to sanity check that the struct members are large enough for the number of channels in the calibration subdevice. Move the MAX_N_CALDACS define to ni_stc.h and set it to the largest number of caldacs (34). The ni_atmio and ni_mio_cs drivers also add one additional member to the private data struct before using the NI_PRIVATE_COMMON macro. For the ni_atmio driver, the struct pnp_dev pointer can be saved in the comedi_device as the 'hw_dev'. The (*detach) of this driver can then use to_pnp_dev() to get it back when detaching the pnp device. In the ni_mio_cs driver, the struct pcmia_device pointer is not used so it can simply be removed. The NI_PRIVATE_COMMON macro can then be converted into a proper struct definition. Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com> Reviewed-by: Ian Abbott <abbotti@mev.co.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-05-29 10:56:30 -07:00
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 https://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
MODULE_LICENSE("GPL");