| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * comedi/drivers/ni_daq_700.c |
| * Driver for DAQCard-700 DIO/AI |
| * copied from 8255 |
| * |
| * COMEDI - Linux Control and Measurement Device Interface |
| * Copyright (C) 1998 David A. Schleef <ds@schleef.org> |
| */ |
| |
| /* |
| * Driver: ni_daq_700 |
| * Description: National Instruments PCMCIA DAQCard-700 |
| * Author: Fred Brooks <nsaspook@nsaspook.com>, |
| * based on ni_daq_dio24 by Daniel Vecino Castel <dvecino@able.es> |
| * Devices: [National Instruments] PCMCIA DAQ-Card-700 (ni_daq_700) |
| * Status: works |
| * Updated: Wed, 21 May 2014 12:07:20 +0000 |
| * |
| * The daqcard-700 appears in Comedi as a digital I/O subdevice (0) with |
| * 16 channels and a analog input subdevice (1) with 16 single-ended channels |
| * or 8 differential channels, and three input ranges. |
| * |
| * Digital: The channel 0 corresponds to the daqcard-700's output |
| * port, bit 0; channel 8 corresponds to the input port, bit 0. |
| * |
| * Digital direction configuration: channels 0-7 output, 8-15 input. |
| * |
| * Analog: The input range is 0 to 4095 with a default of -10 to +10 volts. |
| * Valid ranges: |
| * 0 for -10 to 10V bipolar |
| * 1 for -5 to 5V bipolar |
| * 2 for -2.5 to 2.5V bipolar |
| * |
| * IRQ is assigned but not used. |
| * |
| * Manuals: Register level: https://www.ni.com/pdf/manuals/340698.pdf |
| * User Manual: https://www.ni.com/pdf/manuals/320676d.pdf |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/delay.h> |
| #include <linux/interrupt.h> |
| |
| #include "../comedi_pcmcia.h" |
| |
| /* daqcard700 registers */ |
| #define DIO_W 0x04 /* WO 8bit */ |
| #define DIO_R 0x05 /* RO 8bit */ |
| #define CMD_R1 0x00 /* WO 8bit */ |
| #define CMD_R2 0x07 /* RW 8bit */ |
| #define CMD_R3 0x05 /* W0 8bit */ |
| #define STA_R1 0x00 /* RO 8bit */ |
| #define STA_R2 0x01 /* RO 8bit */ |
| #define ADFIFO_R 0x02 /* RO 16bit */ |
| #define ADCLEAR_R 0x01 /* WO 8bit */ |
| #define CDA_R0 0x08 /* RW 8bit */ |
| #define CDA_R1 0x09 /* RW 8bit */ |
| #define CDA_R2 0x0A /* RW 8bit */ |
| #define CMO_R 0x0B /* RO 8bit */ |
| #define TIC_R 0x06 /* WO 8bit */ |
| /* daqcard700 modes */ |
| #define CMD_R3_DIFF 0x04 /* diff mode */ |
| |
| static const struct comedi_lrange range_daq700_ai = { |
| 3, |
| { |
| BIP_RANGE(10), |
| BIP_RANGE(5), |
| BIP_RANGE(2.5) |
| } |
| }; |
| |
| static int daq700_dio_insn_bits(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| unsigned int mask; |
| unsigned int val; |
| |
| mask = comedi_dio_update_state(s, data); |
| if (mask) { |
| if (mask & 0xff) |
| outb(s->state & 0xff, dev->iobase + DIO_W); |
| } |
| |
| val = s->state & 0xff; |
| val |= inb(dev->iobase + DIO_R) << 8; |
| |
| data[1] = val; |
| |
| return insn->n; |
| } |
| |
| static int daq700_dio_insn_config(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
| { |
| int ret; |
| |
| ret = comedi_dio_insn_config(dev, s, insn, data, 0); |
| if (ret) |
| return ret; |
| |
| /* The DIO channels are not configurable, fix the io_bits */ |
| s->io_bits = 0x00ff; |
| |
| return insn->n; |
| } |
| |
| static int daq700_ai_eoc(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned long context) |
| { |
| unsigned int status; |
| |
| status = inb(dev->iobase + STA_R2); |
| if ((status & 0x03)) |
| return -EOVERFLOW; |
| status = inb(dev->iobase + STA_R1); |
| if ((status & 0x02)) |
| return -ENODATA; |
| if ((status & 0x11) == 0x01) |
| return 0; |
| return -EBUSY; |
| } |
| |
| static int daq700_ai_rinsn(struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, unsigned int *data) |
| { |
| int n; |
| int d; |
| int ret; |
| unsigned int chan = CR_CHAN(insn->chanspec); |
| unsigned int aref = CR_AREF(insn->chanspec); |
| unsigned int range = CR_RANGE(insn->chanspec); |
| unsigned int r3_bits = 0; |
| |
| /* set channel input modes */ |
| if (aref == AREF_DIFF) |
| r3_bits |= CMD_R3_DIFF; |
| /* write channel mode/range */ |
| if (range >= 1) |
| range++; /* convert range to hardware value */ |
| outb(r3_bits | (range & 0x03), dev->iobase + CMD_R3); |
| |
| /* write channel to multiplexer */ |
| /* set mask scan bit high to disable scanning */ |
| outb(chan | 0x80, dev->iobase + CMD_R1); |
| /* mux needs 2us to really settle [Fred Brooks]. */ |
| udelay(2); |
| |
| /* convert n samples */ |
| for (n = 0; n < insn->n; n++) { |
| /* trigger conversion with out0 L to H */ |
| outb(0x00, dev->iobase + CMD_R2); /* enable ADC conversions */ |
| outb(0x30, dev->iobase + CMO_R); /* mode 0 out0 L, from H */ |
| outb(0x00, dev->iobase + ADCLEAR_R); /* clear the ADC FIFO */ |
| /* read 16bit junk from FIFO to clear */ |
| inw(dev->iobase + ADFIFO_R); |
| /* mode 1 out0 H, L to H, start conversion */ |
| outb(0x32, dev->iobase + CMO_R); |
| |
| /* wait for conversion to end */ |
| ret = comedi_timeout(dev, s, insn, daq700_ai_eoc, 0); |
| if (ret) |
| return ret; |
| |
| /* read data */ |
| d = inw(dev->iobase + ADFIFO_R); |
| /* mangle the data as necessary */ |
| /* Bipolar Offset Binary: 0 to 4095 for -10 to +10 */ |
| d &= 0x0fff; |
| d ^= 0x0800; |
| data[n] = d; |
| } |
| return n; |
| } |
| |
| /* |
| * Data acquisition is enabled. |
| * The counter 0 output is high. |
| * The I/O connector pin CLK1 drives counter 1 source. |
| * Multiple-channel scanning is disabled. |
| * All interrupts are disabled. |
| * The analog input range is set to +-10 V |
| * The analog input mode is single-ended. |
| * The analog input circuitry is initialized to channel 0. |
| * The A/D FIFO is cleared. |
| */ |
| static void daq700_ai_config(struct comedi_device *dev, |
| struct comedi_subdevice *s) |
| { |
| unsigned long iobase = dev->iobase; |
| |
| outb(0x80, iobase + CMD_R1); /* disable scanning, ADC to chan 0 */ |
| outb(0x00, iobase + CMD_R2); /* clear all bits */ |
| outb(0x00, iobase + CMD_R3); /* set +-10 range */ |
| outb(0x32, iobase + CMO_R); /* config counter mode1, out0 to H */ |
| outb(0x00, iobase + TIC_R); /* clear counter interrupt */ |
| outb(0x00, iobase + ADCLEAR_R); /* clear the ADC FIFO */ |
| inw(iobase + ADFIFO_R); /* read 16bit junk from FIFO to clear */ |
| } |
| |
| static int daq700_auto_attach(struct comedi_device *dev, |
| unsigned long context) |
| { |
| struct pcmcia_device *link = comedi_to_pcmcia_dev(dev); |
| struct comedi_subdevice *s; |
| int ret; |
| |
| link->config_flags |= CONF_AUTO_SET_IO; |
| ret = comedi_pcmcia_enable(dev, NULL); |
| if (ret) |
| return ret; |
| dev->iobase = link->resource[0]->start; |
| |
| ret = comedi_alloc_subdevices(dev, 2); |
| if (ret) |
| return ret; |
| |
| /* DAQCard-700 dio */ |
| s = &dev->subdevices[0]; |
| s->type = COMEDI_SUBD_DIO; |
| s->subdev_flags = SDF_READABLE | SDF_WRITABLE; |
| s->n_chan = 16; |
| s->range_table = &range_digital; |
| s->maxdata = 1; |
| s->insn_bits = daq700_dio_insn_bits; |
| s->insn_config = daq700_dio_insn_config; |
| s->io_bits = 0x00ff; |
| |
| /* DAQCard-700 ai */ |
| s = &dev->subdevices[1]; |
| s->type = COMEDI_SUBD_AI; |
| s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; |
| s->n_chan = 16; |
| s->maxdata = BIT(12) - 1; |
| s->range_table = &range_daq700_ai; |
| s->insn_read = daq700_ai_rinsn; |
| daq700_ai_config(dev, s); |
| |
| return 0; |
| } |
| |
| static struct comedi_driver daq700_driver = { |
| .driver_name = "ni_daq_700", |
| .module = THIS_MODULE, |
| .auto_attach = daq700_auto_attach, |
| .detach = comedi_pcmcia_disable, |
| }; |
| |
| static int daq700_cs_attach(struct pcmcia_device *link) |
| { |
| return comedi_pcmcia_auto_config(link, &daq700_driver); |
| } |
| |
| static const struct pcmcia_device_id daq700_cs_ids[] = { |
| PCMCIA_DEVICE_MANF_CARD(0x010b, 0x4743), |
| PCMCIA_DEVICE_NULL |
| }; |
| MODULE_DEVICE_TABLE(pcmcia, daq700_cs_ids); |
| |
| static struct pcmcia_driver daq700_cs_driver = { |
| .name = "ni_daq_700", |
| .owner = THIS_MODULE, |
| .id_table = daq700_cs_ids, |
| .probe = daq700_cs_attach, |
| .remove = comedi_pcmcia_auto_unconfig, |
| }; |
| module_comedi_pcmcia_driver(daq700_driver, daq700_cs_driver); |
| |
| MODULE_AUTHOR("Fred Brooks <nsaspook@nsaspook.com>"); |
| MODULE_DESCRIPTION( |
| "Comedi driver for National Instruments PCMCIA DAQCard-700 DIO/AI"); |
| MODULE_LICENSE("GPL"); |