/* comedi/drivers/ni_670x.c Hardware driver for NI 670x devices COMEDI - Linux Control and Measurement Device Interface Copyright (C) 1997-2001 David A. Schleef This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Driver: ni_670x Description: National Instruments 670x Author: Bart Joris Updated: Wed, 11 Dec 2002 18:25:35 -0800 Devices: [National Instruments] PCI-6703 (ni_670x), PCI-6704 Status: unknown Commands are not supported. */ /* Bart Joris Last updated on 20/08/2001 Manuals: 322110a.pdf PCI/PXI-6704 User Manual 322110b.pdf PCI/PXI-6703/6704 User Manual */ /* * Integration with Xenomai/Analogy layer based on the * comedi driver. Adaptation made by * Julien Delange */ #include #include #include #include "../intel/8255.h" #include "ni_mio.h" #include "mite.h" #define PCIMIO_IRQ_POLARITY 1 #define AO_VALUE_OFFSET 0x00 #define AO_CHAN_OFFSET 0x0c #define AO_STATUS_OFFSET 0x10 #define AO_CONTROL_OFFSET 0x10 #define DIO_PORT0_DIR_OFFSET 0x20 #define DIO_PORT0_DATA_OFFSET 0x24 #define DIO_PORT1_DIR_OFFSET 0x28 #define DIO_PORT1_DATA_OFFSET 0x2c #define MISC_STATUS_OFFSET 0x14 #define MISC_CONTROL_OFFSET 0x14 /* Board description*/ struct ni_670x_board { unsigned short device_id; const char *name; unsigned short ao_chans; unsigned short ao_bits; }; #define thisboard ((struct ni_670x_board *)dev->board_ptr) struct ni_670x_private { struct mite_struct *mite; int boardtype; int dio; unsigned int ao_readback[32]; /* * Added when porting to xenomai */ int irq_polarity; int irq_pin; int irq; struct ni_670x_board *board_ptr; /* * END OF ADDED when porting to xenomai */ }; struct ni_670x_subd_priv { int io_bits; unsigned int state; uint16_t readback[2]; uint16_t config; void* counter; }; static int ni_670x_ao_winsn(struct a4l_subdevice *subd, struct a4l_kernel_instruction *insn); static int ni_670x_ao_rinsn(struct a4l_subdevice *subd, struct a4l_kernel_instruction *insn); static int ni_670x_dio_insn_bits(struct a4l_subdevice *subd, struct a4l_kernel_instruction *insn); static int ni_670x_dio_insn_config(struct a4l_subdevice *subd, struct a4l_kernel_instruction *insn); static struct a4l_channels_desc ni_670x_desc_dio = { .mode = A4L_CHAN_GLOBAL_CHANDESC, .length = 8, .chans = { {A4L_CHAN_AREF_GROUND, 1}, }, }; static struct a4l_channels_desc ni_670x_desc_ao = { .mode = A4L_CHAN_GLOBAL_CHANDESC, .length = 0, /* initialized later according to the board found */ .chans = { {A4L_CHAN_AREF_GROUND, 16}, }, }; static struct a4l_rngtab range_0_20mA = { 1, {RANGE_mA(0, 20)} }; static struct a4l_rngtab rng_bipolar10 = { 1, {RANGE_V(-10, 10) }}; struct a4l_rngtab *range_table_list[32] = { &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &rng_bipolar10, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA, &range_0_20mA}; static A4L_RNGDESC(32) ni670x_ao_desc; static void setup_subd_ao(struct a4l_subdevice *subd) { int i; int nchans; nchans = ((struct ni_670x_private*)(subd->dev->priv))->board_ptr->ao_chans; subd->flags = A4L_SUBD_AO; subd->chan_desc = &ni_670x_desc_ao; subd->chan_desc->length = nchans; if (nchans == 32) { subd->rng_desc = (struct a4l_rngdesc*) &ni670x_ao_desc; subd->rng_desc->mode = A4L_RNG_PERCHAN_RNGDESC; for (i = 0 ; i < 16 ; i++) { subd->rng_desc->rngtabs[i] =&rng_bipolar10; subd->rng_desc->rngtabs[16+i] =&range_0_20mA; } } else subd->rng_desc = &a4l_range_bipolar10; subd->insn_write = &ni_670x_ao_winsn; subd->insn_read = &ni_670x_ao_rinsn; } static void setup_subd_dio(struct a4l_subdevice *s) { /* Digital i/o subdevice */ s->flags = A4L_SUBD_DIO; s->chan_desc = &ni_670x_desc_dio; s->rng_desc = &range_digital; s->insn_bits = ni_670x_dio_insn_bits; s->insn_config = ni_670x_dio_insn_config; } struct setup_subd { void (*setup_func) (struct a4l_subdevice *); int sizeof_priv; }; static struct setup_subd setup_subds[2] = { { .setup_func = setup_subd_ao, .sizeof_priv = sizeof(struct ni_670x_subd_priv), }, { .setup_func = setup_subd_dio, .sizeof_priv = sizeof(struct ni_670x_subd_priv), }, }; static const struct ni_670x_board ni_670x_boards[] = { { .device_id = 0x2c90, .name = "PCI-6703", .ao_chans = 16, .ao_bits = 16, }, { .device_id = 0x1920, .name = "PXI-6704", .ao_chans = 32, .ao_bits = 16, }, { .device_id = 0x1290, .name = "PCI-6704", .ao_chans = 32, .ao_bits = 16, }, }; #define n_ni_670x_boards ((sizeof(ni_670x_boards)/sizeof(ni_670x_boards[0]))) static const struct pci_device_id ni_670x_pci_table[] = { {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x2c90)}, {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x1920)}, {0} }; MODULE_DEVICE_TABLE(pci, ni_670x_pci_table); #define devpriv ((struct ni_670x_private *)dev->priv) static inline struct ni_670x_private *private(struct a4l_device *dev) { return (struct ni_670x_private*) dev->priv; } static int ni_670x_attach (struct a4l_device *dev, a4l_lnkdesc_t *arg); static int ni_670x_detach(struct a4l_device *dev); static struct a4l_driver ni_670x_drv = { .owner = THIS_MODULE, .board_name = "analogy_ni_670x", .driver_name = "ni_670x", .attach = ni_670x_attach, .detach = ni_670x_detach, .privdata_size = sizeof(struct ni_670x_private), }; static int __init driver_ni_670x_init_module(void) { return a4l_register_drv (&ni_670x_drv); } static void __exit driver_ni_670x_cleanup_module(void) { a4l_unregister_drv (&ni_670x_drv); } module_init(driver_ni_670x_init_module); module_exit(driver_ni_670x_cleanup_module); static int ni_670x_attach (struct a4l_device *dev, a4l_lnkdesc_t *arg) { int ret, bus, slot, i, irq; struct mite_struct *mite; struct ni_670x_board* board = NULL; int err; if(arg->opts == NULL || arg->opts_size == 0) bus = slot = 0; else { bus = arg->opts_size >= sizeof(unsigned long) ? ((unsigned long *)arg->opts)[0] : 0; slot = arg->opts_size >= sizeof(unsigned long) * 2 ? ((unsigned long *)arg->opts)[1] : 0; } a4l_info(dev, "ni670x attach procedure started(bus=%d/slot=%d)...\n", bus, slot); mite = NULL; for(i = 0; i < n_ni_670x_boards && mite == NULL; i++) { mite = a4l_mite_find_device(bus, slot, ni_670x_boards[i].device_id); board = (struct ni_670x_board*) &ni_670x_boards[i]; } if(mite == NULL) { a4l_err(dev, "%s: cannot find the MITE device\n", __FUNCTION__); return -ENOENT; } a4l_info(dev, "Found device %d %s\n", i, ni_670x_boards[i].name); devpriv->irq_polarity = PCIMIO_IRQ_POLARITY; devpriv->irq_pin = 0; devpriv->mite = mite; devpriv->board_ptr = board; ret = a4l_mite_setup(devpriv->mite, 0); if (ret < 0) { a4l_err(dev, "%s: error setting up mite\n", __FUNCTION__); return ret; } irq = mite_irq(devpriv->mite); devpriv->irq = irq; a4l_info(dev, "found %s board\n", board->name); for (i = 0; i < 2; i++) { struct a4l_subdevice *subd = a4l_alloc_subd(setup_subds[i].sizeof_priv, NULL); if (subd == NULL) { a4l_err(dev, "%s: cannot allocate subdevice\n", __FUNCTION__); return -ENOMEM; } err = a4l_add_subd(dev, subd); if (err != i) { a4l_err(dev, "%s: cannot add subdevice\n", __FUNCTION__); return err; } setup_subds[i].setup_func (subd); } /* Config of misc registers */ writel(0x10, devpriv->mite->daq_io_addr + MISC_CONTROL_OFFSET); /* Config of ao registers */ writel(0x00, devpriv->mite->daq_io_addr + AO_CONTROL_OFFSET); a4l_info(dev, "ni670x attached\n"); return 0; } static int ni_670x_detach(struct a4l_device *dev) { a4l_info(dev, "ni670x detach procedure started...\n"); if(dev->priv != NULL && devpriv->mite != NULL) a4l_mite_unsetup(devpriv->mite); a4l_info(dev, "ni670x detach procedure succeeded...\n"); return 0; } static int ni_670x_dio_insn_config(struct a4l_subdevice *subd, struct a4l_kernel_instruction *insn) { struct a4l_device *dev = subd->dev; unsigned int *data = (unsigned int *)insn->data; int chan = CR_CHAN(insn->chan_desc); struct ni_670x_subd_priv *subdpriv = (struct ni_670x_subd_priv *)subd->priv; switch (data[0]) { case A4L_INSN_CONFIG_DIO_OUTPUT: subdpriv->io_bits |= 1 << chan; break; case A4L_INSN_CONFIG_DIO_INPUT: subdpriv->io_bits &= ~(1 << chan); break; case A4L_INSN_CONFIG_DIO_QUERY: data[1] = (subdpriv->io_bits & (1 << chan)) ? A4L_OUTPUT : A4L_INPUT; return 0; break; default: return -EINVAL; break; } writel(subdpriv->io_bits, devpriv->mite->daq_io_addr + DIO_PORT0_DIR_OFFSET); return 0; } static int ni_670x_ao_winsn(struct a4l_subdevice *subd, struct a4l_kernel_instruction *insn) { int i; unsigned int tmp; unsigned int* dtmp; int chan; dtmp = (unsigned int*)insn->data; chan = CR_CHAN(insn->chan_desc); /* Channel number mapping : NI 6703/ NI 6704 | NI 6704 Only ---------------------------------------------------- vch(0) : 0 | ich(16) : 1 vch(1) : 2 | ich(17) : 3 . : . | . . . : . | . . . : . | . . vch(15) : 30 | ich(31) : 31 */ for (i = 0; i < insn->data_size / sizeof(unsigned int); i++) { tmp = dtmp[i]; /* First write in channel register which channel to use */ writel(((chan & 15) << 1) | ((chan & 16) >> 4), private (subd->dev)->mite->daq_io_addr + AO_CHAN_OFFSET); /* write channel value */ writel(dtmp[i], private(subd->dev)->mite->daq_io_addr + AO_VALUE_OFFSET); private(subd->dev)->ao_readback[chan] = tmp; } return 0; } static int ni_670x_ao_rinsn(struct a4l_subdevice *subd, struct a4l_kernel_instruction *insn) { int i; unsigned int* dtmp; int chan = CR_CHAN(insn->chan_desc); dtmp = (unsigned int*)insn->data; for (i = 0; i < insn->data_size / sizeof(unsigned int); i++) dtmp[i] = private(subd->dev)->ao_readback[chan]; return 0; } static int ni_670x_dio_insn_bits(struct a4l_subdevice *subd, struct a4l_kernel_instruction *insn) { return -ENOSYS; } MODULE_DESCRIPTION("Analogy driver for NI670x series cards"); MODULE_LICENSE("GPL");