From 6f47802c1dacb30879654237231df573a50f3a4d Mon Sep 17 00:00:00 2001 From: David Schleef Date: Thu, 19 Feb 2009 09:40:31 -0800 Subject: Staging: comedi: add ni_6527 driver From: David Schleef Driver for National Instruments PCI-6527 From: David Schleef Cc: Ian Abbott Cc: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/ni_6527.c | 486 +++++++++++++++++++++++++++++++ 1 file changed, 486 insertions(+) --- /dev/null +++ b/drivers/staging/comedi/drivers/ni_6527.c @@ -0,0 +1,486 @@ +/* + comedi/drivers/ni_6527.c + driver for National Instruments PCI-6527 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1999,2002,2003 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_6527 +Description: National Instruments 6527 +Author: ds +Status: works +Devices: [National Instruments] PCI-6527 (ni6527), PXI-6527 +Updated: Sat, 25 Jan 2003 13:24:40 -0800 + + +*/ + +/* + Manuals (available from ftp://ftp.natinst.com/support/manuals) + + 370106b.pdf 6527 Register Level Programmer Manual + + */ + +#define DEBUG 1 +#define DEBUG_FLAGS + +#include "../comedidev.h" + +#include "mite.h" + +#define NI6527_DIO_SIZE 4096 +#define NI6527_MITE_SIZE 4096 + +#define Port_Register(x) (0x00+(x)) +#define ID_Register 0x06 + +#define Clear_Register 0x07 +#define ClrEdge 0x08 +#define ClrOverflow 0x04 +#define ClrFilter 0x02 +#define ClrInterval 0x01 + +#define Filter_Interval(x) (0x08+(x)) +#define Filter_Enable(x) (0x0c+(x)) + +#define Change_Status 0x14 +#define MasterInterruptStatus 0x04 +#define Overflow 0x02 +#define EdgeStatus 0x01 + +#define Master_Interrupt_Control 0x15 +#define FallingEdgeIntEnable 0x10 +#define RisingEdgeIntEnable 0x08 +#define MasterInterruptEnable 0x04 +#define OverflowIntEnable 0x02 +#define EdgeIntEnable 0x01 + +#define Rising_Edge_Detection_Enable(x) (0x018+(x)) +#define Falling_Edge_Detection_Enable(x) (0x020+(x)) + +static int ni6527_attach(comedi_device * dev, comedi_devconfig * it); +static int ni6527_detach(comedi_device * dev); +static comedi_driver driver_ni6527 = { + driver_name:"ni6527", + module:THIS_MODULE, + attach:ni6527_attach, + detach:ni6527_detach, +}; + +typedef struct { + int dev_id; + const char *name; +} ni6527_board; +static const ni6527_board ni6527_boards[] = { + { + dev_id: 0x2b20, + name: "pci-6527", + }, + { + dev_id: 0x2b10, + name: "pxi-6527", + }, +}; + +#define n_ni6527_boards (sizeof(ni6527_boards)/sizeof(ni6527_boards[0])) +#define this_board ((const ni6527_board *)dev->board_ptr) + +static DEFINE_PCI_DEVICE_TABLE(ni6527_pci_table) = { + {PCI_VENDOR_ID_NATINST, 0x2b10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_NATINST, 0x2b20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0} +}; + +MODULE_DEVICE_TABLE(pci, ni6527_pci_table); + +typedef struct { + struct mite_struct *mite; + unsigned int filter_interval; + unsigned int filter_enable; +} ni6527_private; +#define devpriv ((ni6527_private *)dev->private) + +static int ni6527_find_device(comedi_device * dev, int bus, int slot); + +static int ni6527_di_insn_config(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int chan = CR_CHAN(insn->chanspec); + unsigned int interval; + + if (insn->n != 2) + return -EINVAL; + + if (data[0] != INSN_CONFIG_FILTER) + return -EINVAL; + + if (data[1]) { + interval = (data[1] + 100) / 200; + data[1] = interval * 200; + + if (interval != devpriv->filter_interval) { + writeb(interval & 0xff, + devpriv->mite->daq_io_addr + + Filter_Interval(0)); + writeb((interval >> 8) & 0xff, + devpriv->mite->daq_io_addr + + Filter_Interval(1)); + writeb((interval >> 16) & 0x0f, + devpriv->mite->daq_io_addr + + Filter_Interval(2)); + + writeb(ClrInterval, + devpriv->mite->daq_io_addr + Clear_Register); + + devpriv->filter_interval = interval; + } + + devpriv->filter_enable |= 1 << chan; + } else { + devpriv->filter_enable &= ~(1 << chan); + } + + writeb(devpriv->filter_enable, + devpriv->mite->daq_io_addr + Filter_Enable(0)); + writeb(devpriv->filter_enable >> 8, + devpriv->mite->daq_io_addr + Filter_Enable(1)); + writeb(devpriv->filter_enable >> 16, + devpriv->mite->daq_io_addr + Filter_Enable(2)); + + return 2; +} + +static int ni6527_di_insn_bits(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + if (insn->n != 2) + return -EINVAL; + + data[1] = readb(devpriv->mite->daq_io_addr + Port_Register(0)); + data[1] |= readb(devpriv->mite->daq_io_addr + Port_Register(1)) << 8; + data[1] |= readb(devpriv->mite->daq_io_addr + Port_Register(2)) << 16; + + return 2; +} + +static int ni6527_do_insn_bits(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + if (insn->n != 2) + return -EINVAL; + if (data[0]) { + s->state &= ~data[0]; + s->state |= (data[0] & data[1]); + + /* The open relay state on the board cooresponds to 1, + * but in Comedi, it is represented by 0. */ + if (data[0] & 0x0000ff) { + writeb((s->state ^ 0xff), + devpriv->mite->daq_io_addr + Port_Register(3)); + } + if (data[0] & 0x00ff00) { + writeb((s->state >> 8) ^ 0xff, + devpriv->mite->daq_io_addr + Port_Register(4)); + } + if (data[0] & 0xff0000) { + writeb((s->state >> 16) ^ 0xff, + devpriv->mite->daq_io_addr + Port_Register(5)); + } + } + data[1] = s->state; + + return 2; +} + +static irqreturn_t ni6527_interrupt(int irq, void *d PT_REGS_ARG) +{ + comedi_device *dev = d; + comedi_subdevice *s = dev->subdevices + 2; + unsigned int status; + + status = readb(devpriv->mite->daq_io_addr + Change_Status); + if ((status & MasterInterruptStatus) == 0) + return IRQ_NONE; + if ((status & EdgeStatus) == 0) + return IRQ_NONE; + + writeb(ClrEdge | ClrOverflow, + devpriv->mite->daq_io_addr + Clear_Register); + + comedi_buf_put(s->async, 0); + s->async->events |= COMEDI_CB_EOS; + comedi_event(dev, s); + return IRQ_HANDLED; +} + +static int ni6527_intr_cmdtest(comedi_device * dev, comedi_subdevice * s, + comedi_cmd * cmd) +{ + int err = 0; + int tmp; + + /* step 1: make sure trigger sources are trivially valid */ + + tmp = cmd->start_src; + cmd->start_src &= TRIG_NOW; + if (!cmd->start_src || tmp != cmd->start_src) + err++; + + tmp = cmd->scan_begin_src; + cmd->scan_begin_src &= TRIG_OTHER; + if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) + err++; + + tmp = cmd->convert_src; + cmd->convert_src &= TRIG_FOLLOW; + if (!cmd->convert_src || tmp != cmd->convert_src) + err++; + + tmp = cmd->scan_end_src; + cmd->scan_end_src &= TRIG_COUNT; + if (!cmd->scan_end_src || tmp != cmd->scan_end_src) + err++; + + tmp = cmd->stop_src; + cmd->stop_src &= TRIG_COUNT; + if (!cmd->stop_src || tmp != cmd->stop_src) + err++; + + if (err) + return 1; + + /* step 2: make sure trigger sources are unique and mutually compatible */ + + if (err) + return 2; + + /* step 3: make sure arguments are trivially compatible */ + + if (cmd->start_arg != 0) { + cmd->start_arg = 0; + err++; + } + if (cmd->scan_begin_arg != 0) { + cmd->scan_begin_arg = 0; + err++; + } + if (cmd->convert_arg != 0) { + cmd->convert_arg = 0; + err++; + } + + if (cmd->scan_end_arg != 1) { + cmd->scan_end_arg = 1; + err++; + } + if (cmd->stop_arg != 0) { + cmd->stop_arg = 0; + err++; + } + + if (err) + return 3; + + /* step 4: fix up any arguments */ + + if (err) + return 4; + + return 0; +} + +static int ni6527_intr_cmd(comedi_device * dev, comedi_subdevice * s) +{ + //comedi_cmd *cmd = &s->async->cmd; + + writeb(ClrEdge | ClrOverflow, + devpriv->mite->daq_io_addr + Clear_Register); + writeb(FallingEdgeIntEnable | RisingEdgeIntEnable | + MasterInterruptEnable | EdgeIntEnable, + devpriv->mite->daq_io_addr + Master_Interrupt_Control); + + return 0; +} + +static int ni6527_intr_cancel(comedi_device * dev, comedi_subdevice * s) +{ + writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control); + + return 0; +} + +static int ni6527_intr_insn_bits(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + if (insn->n < 1) + return -EINVAL; + + data[1] = 0; + return 2; +} + +static int ni6527_intr_insn_config(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + if (insn->n < 1) + return -EINVAL; + if (data[0] != INSN_CONFIG_CHANGE_NOTIFY) + return -EINVAL; + + writeb(data[1], + devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(0)); + writeb(data[1] >> 8, + devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(1)); + writeb(data[1] >> 16, + devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(2)); + + writeb(data[2], + devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(0)); + writeb(data[2] >> 8, + devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(1)); + writeb(data[2] >> 16, + devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(2)); + + return 2; +} + +static int ni6527_attach(comedi_device * dev, comedi_devconfig * it) +{ + comedi_subdevice *s; + int ret; + + printk("comedi%d: ni6527:", dev->minor); + + if ((ret = alloc_private(dev, sizeof(ni6527_private))) < 0) + return ret; + + ret = ni6527_find_device(dev, it->options[0], it->options[1]); + if (ret < 0) + return ret; + + ret = mite_setup(devpriv->mite); + if (ret < 0) { + printk("error setting up mite\n"); + return ret; + } + + dev->board_name = this_board->name; + printk(" %s", dev->board_name); + + printk(" ID=0x%02x", readb(devpriv->mite->daq_io_addr + ID_Register)); + + if ((ret = alloc_subdevices(dev, 3)) < 0) + return ret; + + s = dev->subdevices + 0; + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 24; + s->range_table = &range_digital; + s->maxdata = 1; + s->insn_config = ni6527_di_insn_config; + s->insn_bits = ni6527_di_insn_bits; + + s = dev->subdevices + 1; + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE; + s->n_chan = 24; + s->range_table = &range_unknown; /* FIXME: actually conductance */ + s->maxdata = 1; + s->insn_bits = ni6527_do_insn_bits; + + s = dev->subdevices + 2; + dev->read_subdev = s; + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE | SDF_CMD_READ; + s->n_chan = 1; + s->range_table = &range_unknown; + s->maxdata = 1; + s->do_cmdtest = ni6527_intr_cmdtest; + s->do_cmd = ni6527_intr_cmd; + s->cancel = ni6527_intr_cancel; + s->insn_bits = ni6527_intr_insn_bits; + s->insn_config = ni6527_intr_insn_config; + + writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(0)); + writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(1)); + writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(2)); + + writeb(ClrEdge | ClrOverflow | ClrFilter | ClrInterval, + devpriv->mite->daq_io_addr + Clear_Register); + writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control); + + ret = comedi_request_irq(mite_irq(devpriv->mite), ni6527_interrupt, + IRQF_SHARED, "ni6527", dev); + if (ret < 0) { + printk(" irq not available"); + } else + dev->irq = mite_irq(devpriv->mite); + + printk("\n"); + + return 0; +} + +static int ni6527_detach(comedi_device * dev) +{ + if (devpriv && devpriv->mite && devpriv->mite->daq_io_addr) { + writeb(0x00, + devpriv->mite->daq_io_addr + Master_Interrupt_Control); + } + + if (dev->irq) { + comedi_free_irq(dev->irq, dev); + } + + if (devpriv && devpriv->mite) { + mite_unsetup(devpriv->mite); + } + + return 0; +} + +static int ni6527_find_device(comedi_device * dev, int bus, int slot) +{ + struct mite_struct *mite; + int i; + + for (mite = mite_devices; mite; mite = mite->next) { + if (mite->used) + continue; + if (bus || slot) { + if (bus != mite->pcidev->bus->number || + slot != PCI_SLOT(mite->pcidev->devfn)) + continue; + } + for (i = 0; i < n_ni6527_boards; i++) { + if (mite_device_id(mite) == ni6527_boards[i].dev_id) { + dev->board_ptr = ni6527_boards + i; + devpriv->mite = mite; + return 0; + } + } + } + printk("no device found\n"); + mite_list_devices(); + return -EIO; +} + +COMEDI_PCI_INITCLEANUP(driver_ni6527, ni6527_pci_table);