1*8ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 2*8ffdff6aSGreg Kroah-Hartman /* 3*8ffdff6aSGreg Kroah-Hartman * comedi/drivers/ni_daq_700.c 4*8ffdff6aSGreg Kroah-Hartman * Driver for DAQCard-700 DIO/AI 5*8ffdff6aSGreg Kroah-Hartman * copied from 8255 6*8ffdff6aSGreg Kroah-Hartman * 7*8ffdff6aSGreg Kroah-Hartman * COMEDI - Linux Control and Measurement Device Interface 8*8ffdff6aSGreg Kroah-Hartman * Copyright (C) 1998 David A. Schleef <ds@schleef.org> 9*8ffdff6aSGreg Kroah-Hartman */ 10*8ffdff6aSGreg Kroah-Hartman 11*8ffdff6aSGreg Kroah-Hartman /* 12*8ffdff6aSGreg Kroah-Hartman * Driver: ni_daq_700 13*8ffdff6aSGreg Kroah-Hartman * Description: National Instruments PCMCIA DAQCard-700 14*8ffdff6aSGreg Kroah-Hartman * Author: Fred Brooks <nsaspook@nsaspook.com>, 15*8ffdff6aSGreg Kroah-Hartman * based on ni_daq_dio24 by Daniel Vecino Castel <dvecino@able.es> 16*8ffdff6aSGreg Kroah-Hartman * Devices: [National Instruments] PCMCIA DAQ-Card-700 (ni_daq_700) 17*8ffdff6aSGreg Kroah-Hartman * Status: works 18*8ffdff6aSGreg Kroah-Hartman * Updated: Wed, 21 May 2014 12:07:20 +0000 19*8ffdff6aSGreg Kroah-Hartman * 20*8ffdff6aSGreg Kroah-Hartman * The daqcard-700 appears in Comedi as a digital I/O subdevice (0) with 21*8ffdff6aSGreg Kroah-Hartman * 16 channels and a analog input subdevice (1) with 16 single-ended channels 22*8ffdff6aSGreg Kroah-Hartman * or 8 differential channels, and three input ranges. 23*8ffdff6aSGreg Kroah-Hartman * 24*8ffdff6aSGreg Kroah-Hartman * Digital: The channel 0 corresponds to the daqcard-700's output 25*8ffdff6aSGreg Kroah-Hartman * port, bit 0; channel 8 corresponds to the input port, bit 0. 26*8ffdff6aSGreg Kroah-Hartman * 27*8ffdff6aSGreg Kroah-Hartman * Digital direction configuration: channels 0-7 output, 8-15 input. 28*8ffdff6aSGreg Kroah-Hartman * 29*8ffdff6aSGreg Kroah-Hartman * Analog: The input range is 0 to 4095 with a default of -10 to +10 volts. 30*8ffdff6aSGreg Kroah-Hartman * Valid ranges: 31*8ffdff6aSGreg Kroah-Hartman * 0 for -10 to 10V bipolar 32*8ffdff6aSGreg Kroah-Hartman * 1 for -5 to 5V bipolar 33*8ffdff6aSGreg Kroah-Hartman * 2 for -2.5 to 2.5V bipolar 34*8ffdff6aSGreg Kroah-Hartman * 35*8ffdff6aSGreg Kroah-Hartman * IRQ is assigned but not used. 36*8ffdff6aSGreg Kroah-Hartman * 37*8ffdff6aSGreg Kroah-Hartman * Manuals: Register level: https://www.ni.com/pdf/manuals/340698.pdf 38*8ffdff6aSGreg Kroah-Hartman * User Manual: https://www.ni.com/pdf/manuals/320676d.pdf 39*8ffdff6aSGreg Kroah-Hartman */ 40*8ffdff6aSGreg Kroah-Hartman 41*8ffdff6aSGreg Kroah-Hartman #include <linux/module.h> 42*8ffdff6aSGreg Kroah-Hartman #include <linux/delay.h> 43*8ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h> 44*8ffdff6aSGreg Kroah-Hartman 45*8ffdff6aSGreg Kroah-Hartman #include "../comedi_pcmcia.h" 46*8ffdff6aSGreg Kroah-Hartman 47*8ffdff6aSGreg Kroah-Hartman /* daqcard700 registers */ 48*8ffdff6aSGreg Kroah-Hartman #define DIO_W 0x04 /* WO 8bit */ 49*8ffdff6aSGreg Kroah-Hartman #define DIO_R 0x05 /* RO 8bit */ 50*8ffdff6aSGreg Kroah-Hartman #define CMD_R1 0x00 /* WO 8bit */ 51*8ffdff6aSGreg Kroah-Hartman #define CMD_R2 0x07 /* RW 8bit */ 52*8ffdff6aSGreg Kroah-Hartman #define CMD_R3 0x05 /* W0 8bit */ 53*8ffdff6aSGreg Kroah-Hartman #define STA_R1 0x00 /* RO 8bit */ 54*8ffdff6aSGreg Kroah-Hartman #define STA_R2 0x01 /* RO 8bit */ 55*8ffdff6aSGreg Kroah-Hartman #define ADFIFO_R 0x02 /* RO 16bit */ 56*8ffdff6aSGreg Kroah-Hartman #define ADCLEAR_R 0x01 /* WO 8bit */ 57*8ffdff6aSGreg Kroah-Hartman #define CDA_R0 0x08 /* RW 8bit */ 58*8ffdff6aSGreg Kroah-Hartman #define CDA_R1 0x09 /* RW 8bit */ 59*8ffdff6aSGreg Kroah-Hartman #define CDA_R2 0x0A /* RW 8bit */ 60*8ffdff6aSGreg Kroah-Hartman #define CMO_R 0x0B /* RO 8bit */ 61*8ffdff6aSGreg Kroah-Hartman #define TIC_R 0x06 /* WO 8bit */ 62*8ffdff6aSGreg Kroah-Hartman /* daqcard700 modes */ 63*8ffdff6aSGreg Kroah-Hartman #define CMD_R3_DIFF 0x04 /* diff mode */ 64*8ffdff6aSGreg Kroah-Hartman 65*8ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange range_daq700_ai = { 66*8ffdff6aSGreg Kroah-Hartman 3, 67*8ffdff6aSGreg Kroah-Hartman { 68*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(10), 69*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(5), 70*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(2.5) 71*8ffdff6aSGreg Kroah-Hartman } 72*8ffdff6aSGreg Kroah-Hartman }; 73*8ffdff6aSGreg Kroah-Hartman 74*8ffdff6aSGreg Kroah-Hartman static int daq700_dio_insn_bits(struct comedi_device *dev, 75*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 76*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 77*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 78*8ffdff6aSGreg Kroah-Hartman { 79*8ffdff6aSGreg Kroah-Hartman unsigned int mask; 80*8ffdff6aSGreg Kroah-Hartman unsigned int val; 81*8ffdff6aSGreg Kroah-Hartman 82*8ffdff6aSGreg Kroah-Hartman mask = comedi_dio_update_state(s, data); 83*8ffdff6aSGreg Kroah-Hartman if (mask) { 84*8ffdff6aSGreg Kroah-Hartman if (mask & 0xff) 85*8ffdff6aSGreg Kroah-Hartman outb(s->state & 0xff, dev->iobase + DIO_W); 86*8ffdff6aSGreg Kroah-Hartman } 87*8ffdff6aSGreg Kroah-Hartman 88*8ffdff6aSGreg Kroah-Hartman val = s->state & 0xff; 89*8ffdff6aSGreg Kroah-Hartman val |= inb(dev->iobase + DIO_R) << 8; 90*8ffdff6aSGreg Kroah-Hartman 91*8ffdff6aSGreg Kroah-Hartman data[1] = val; 92*8ffdff6aSGreg Kroah-Hartman 93*8ffdff6aSGreg Kroah-Hartman return insn->n; 94*8ffdff6aSGreg Kroah-Hartman } 95*8ffdff6aSGreg Kroah-Hartman 96*8ffdff6aSGreg Kroah-Hartman static int daq700_dio_insn_config(struct comedi_device *dev, 97*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 98*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 99*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 100*8ffdff6aSGreg Kroah-Hartman { 101*8ffdff6aSGreg Kroah-Hartman int ret; 102*8ffdff6aSGreg Kroah-Hartman 103*8ffdff6aSGreg Kroah-Hartman ret = comedi_dio_insn_config(dev, s, insn, data, 0); 104*8ffdff6aSGreg Kroah-Hartman if (ret) 105*8ffdff6aSGreg Kroah-Hartman return ret; 106*8ffdff6aSGreg Kroah-Hartman 107*8ffdff6aSGreg Kroah-Hartman /* The DIO channels are not configurable, fix the io_bits */ 108*8ffdff6aSGreg Kroah-Hartman s->io_bits = 0x00ff; 109*8ffdff6aSGreg Kroah-Hartman 110*8ffdff6aSGreg Kroah-Hartman return insn->n; 111*8ffdff6aSGreg Kroah-Hartman } 112*8ffdff6aSGreg Kroah-Hartman 113*8ffdff6aSGreg Kroah-Hartman static int daq700_ai_eoc(struct comedi_device *dev, 114*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 115*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 116*8ffdff6aSGreg Kroah-Hartman unsigned long context) 117*8ffdff6aSGreg Kroah-Hartman { 118*8ffdff6aSGreg Kroah-Hartman unsigned int status; 119*8ffdff6aSGreg Kroah-Hartman 120*8ffdff6aSGreg Kroah-Hartman status = inb(dev->iobase + STA_R2); 121*8ffdff6aSGreg Kroah-Hartman if ((status & 0x03)) 122*8ffdff6aSGreg Kroah-Hartman return -EOVERFLOW; 123*8ffdff6aSGreg Kroah-Hartman status = inb(dev->iobase + STA_R1); 124*8ffdff6aSGreg Kroah-Hartman if ((status & 0x02)) 125*8ffdff6aSGreg Kroah-Hartman return -ENODATA; 126*8ffdff6aSGreg Kroah-Hartman if ((status & 0x11) == 0x01) 127*8ffdff6aSGreg Kroah-Hartman return 0; 128*8ffdff6aSGreg Kroah-Hartman return -EBUSY; 129*8ffdff6aSGreg Kroah-Hartman } 130*8ffdff6aSGreg Kroah-Hartman 131*8ffdff6aSGreg Kroah-Hartman static int daq700_ai_rinsn(struct comedi_device *dev, 132*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 133*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, unsigned int *data) 134*8ffdff6aSGreg Kroah-Hartman { 135*8ffdff6aSGreg Kroah-Hartman int n; 136*8ffdff6aSGreg Kroah-Hartman int d; 137*8ffdff6aSGreg Kroah-Hartman int ret; 138*8ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 139*8ffdff6aSGreg Kroah-Hartman unsigned int aref = CR_AREF(insn->chanspec); 140*8ffdff6aSGreg Kroah-Hartman unsigned int range = CR_RANGE(insn->chanspec); 141*8ffdff6aSGreg Kroah-Hartman unsigned int r3_bits = 0; 142*8ffdff6aSGreg Kroah-Hartman 143*8ffdff6aSGreg Kroah-Hartman /* set channel input modes */ 144*8ffdff6aSGreg Kroah-Hartman if (aref == AREF_DIFF) 145*8ffdff6aSGreg Kroah-Hartman r3_bits |= CMD_R3_DIFF; 146*8ffdff6aSGreg Kroah-Hartman /* write channel mode/range */ 147*8ffdff6aSGreg Kroah-Hartman if (range >= 1) 148*8ffdff6aSGreg Kroah-Hartman range++; /* convert range to hardware value */ 149*8ffdff6aSGreg Kroah-Hartman outb(r3_bits | (range & 0x03), dev->iobase + CMD_R3); 150*8ffdff6aSGreg Kroah-Hartman 151*8ffdff6aSGreg Kroah-Hartman /* write channel to multiplexer */ 152*8ffdff6aSGreg Kroah-Hartman /* set mask scan bit high to disable scanning */ 153*8ffdff6aSGreg Kroah-Hartman outb(chan | 0x80, dev->iobase + CMD_R1); 154*8ffdff6aSGreg Kroah-Hartman /* mux needs 2us to really settle [Fred Brooks]. */ 155*8ffdff6aSGreg Kroah-Hartman udelay(2); 156*8ffdff6aSGreg Kroah-Hartman 157*8ffdff6aSGreg Kroah-Hartman /* convert n samples */ 158*8ffdff6aSGreg Kroah-Hartman for (n = 0; n < insn->n; n++) { 159*8ffdff6aSGreg Kroah-Hartman /* trigger conversion with out0 L to H */ 160*8ffdff6aSGreg Kroah-Hartman outb(0x00, dev->iobase + CMD_R2); /* enable ADC conversions */ 161*8ffdff6aSGreg Kroah-Hartman outb(0x30, dev->iobase + CMO_R); /* mode 0 out0 L, from H */ 162*8ffdff6aSGreg Kroah-Hartman outb(0x00, dev->iobase + ADCLEAR_R); /* clear the ADC FIFO */ 163*8ffdff6aSGreg Kroah-Hartman /* read 16bit junk from FIFO to clear */ 164*8ffdff6aSGreg Kroah-Hartman inw(dev->iobase + ADFIFO_R); 165*8ffdff6aSGreg Kroah-Hartman /* mode 1 out0 H, L to H, start conversion */ 166*8ffdff6aSGreg Kroah-Hartman outb(0x32, dev->iobase + CMO_R); 167*8ffdff6aSGreg Kroah-Hartman 168*8ffdff6aSGreg Kroah-Hartman /* wait for conversion to end */ 169*8ffdff6aSGreg Kroah-Hartman ret = comedi_timeout(dev, s, insn, daq700_ai_eoc, 0); 170*8ffdff6aSGreg Kroah-Hartman if (ret) 171*8ffdff6aSGreg Kroah-Hartman return ret; 172*8ffdff6aSGreg Kroah-Hartman 173*8ffdff6aSGreg Kroah-Hartman /* read data */ 174*8ffdff6aSGreg Kroah-Hartman d = inw(dev->iobase + ADFIFO_R); 175*8ffdff6aSGreg Kroah-Hartman /* mangle the data as necessary */ 176*8ffdff6aSGreg Kroah-Hartman /* Bipolar Offset Binary: 0 to 4095 for -10 to +10 */ 177*8ffdff6aSGreg Kroah-Hartman d &= 0x0fff; 178*8ffdff6aSGreg Kroah-Hartman d ^= 0x0800; 179*8ffdff6aSGreg Kroah-Hartman data[n] = d; 180*8ffdff6aSGreg Kroah-Hartman } 181*8ffdff6aSGreg Kroah-Hartman return n; 182*8ffdff6aSGreg Kroah-Hartman } 183*8ffdff6aSGreg Kroah-Hartman 184*8ffdff6aSGreg Kroah-Hartman /* 185*8ffdff6aSGreg Kroah-Hartman * Data acquisition is enabled. 186*8ffdff6aSGreg Kroah-Hartman * The counter 0 output is high. 187*8ffdff6aSGreg Kroah-Hartman * The I/O connector pin CLK1 drives counter 1 source. 188*8ffdff6aSGreg Kroah-Hartman * Multiple-channel scanning is disabled. 189*8ffdff6aSGreg Kroah-Hartman * All interrupts are disabled. 190*8ffdff6aSGreg Kroah-Hartman * The analog input range is set to +-10 V 191*8ffdff6aSGreg Kroah-Hartman * The analog input mode is single-ended. 192*8ffdff6aSGreg Kroah-Hartman * The analog input circuitry is initialized to channel 0. 193*8ffdff6aSGreg Kroah-Hartman * The A/D FIFO is cleared. 194*8ffdff6aSGreg Kroah-Hartman */ 195*8ffdff6aSGreg Kroah-Hartman static void daq700_ai_config(struct comedi_device *dev, 196*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 197*8ffdff6aSGreg Kroah-Hartman { 198*8ffdff6aSGreg Kroah-Hartman unsigned long iobase = dev->iobase; 199*8ffdff6aSGreg Kroah-Hartman 200*8ffdff6aSGreg Kroah-Hartman outb(0x80, iobase + CMD_R1); /* disable scanning, ADC to chan 0 */ 201*8ffdff6aSGreg Kroah-Hartman outb(0x00, iobase + CMD_R2); /* clear all bits */ 202*8ffdff6aSGreg Kroah-Hartman outb(0x00, iobase + CMD_R3); /* set +-10 range */ 203*8ffdff6aSGreg Kroah-Hartman outb(0x32, iobase + CMO_R); /* config counter mode1, out0 to H */ 204*8ffdff6aSGreg Kroah-Hartman outb(0x00, iobase + TIC_R); /* clear counter interrupt */ 205*8ffdff6aSGreg Kroah-Hartman outb(0x00, iobase + ADCLEAR_R); /* clear the ADC FIFO */ 206*8ffdff6aSGreg Kroah-Hartman inw(iobase + ADFIFO_R); /* read 16bit junk from FIFO to clear */ 207*8ffdff6aSGreg Kroah-Hartman } 208*8ffdff6aSGreg Kroah-Hartman 209*8ffdff6aSGreg Kroah-Hartman static int daq700_auto_attach(struct comedi_device *dev, 210*8ffdff6aSGreg Kroah-Hartman unsigned long context) 211*8ffdff6aSGreg Kroah-Hartman { 212*8ffdff6aSGreg Kroah-Hartman struct pcmcia_device *link = comedi_to_pcmcia_dev(dev); 213*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 214*8ffdff6aSGreg Kroah-Hartman int ret; 215*8ffdff6aSGreg Kroah-Hartman 216*8ffdff6aSGreg Kroah-Hartman link->config_flags |= CONF_AUTO_SET_IO; 217*8ffdff6aSGreg Kroah-Hartman ret = comedi_pcmcia_enable(dev, NULL); 218*8ffdff6aSGreg Kroah-Hartman if (ret) 219*8ffdff6aSGreg Kroah-Hartman return ret; 220*8ffdff6aSGreg Kroah-Hartman dev->iobase = link->resource[0]->start; 221*8ffdff6aSGreg Kroah-Hartman 222*8ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdevices(dev, 2); 223*8ffdff6aSGreg Kroah-Hartman if (ret) 224*8ffdff6aSGreg Kroah-Hartman return ret; 225*8ffdff6aSGreg Kroah-Hartman 226*8ffdff6aSGreg Kroah-Hartman /* DAQCard-700 dio */ 227*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[0]; 228*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_DIO; 229*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 230*8ffdff6aSGreg Kroah-Hartman s->n_chan = 16; 231*8ffdff6aSGreg Kroah-Hartman s->range_table = &range_digital; 232*8ffdff6aSGreg Kroah-Hartman s->maxdata = 1; 233*8ffdff6aSGreg Kroah-Hartman s->insn_bits = daq700_dio_insn_bits; 234*8ffdff6aSGreg Kroah-Hartman s->insn_config = daq700_dio_insn_config; 235*8ffdff6aSGreg Kroah-Hartman s->io_bits = 0x00ff; 236*8ffdff6aSGreg Kroah-Hartman 237*8ffdff6aSGreg Kroah-Hartman /* DAQCard-700 ai */ 238*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[1]; 239*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AI; 240*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; 241*8ffdff6aSGreg Kroah-Hartman s->n_chan = 16; 242*8ffdff6aSGreg Kroah-Hartman s->maxdata = BIT(12) - 1; 243*8ffdff6aSGreg Kroah-Hartman s->range_table = &range_daq700_ai; 244*8ffdff6aSGreg Kroah-Hartman s->insn_read = daq700_ai_rinsn; 245*8ffdff6aSGreg Kroah-Hartman daq700_ai_config(dev, s); 246*8ffdff6aSGreg Kroah-Hartman 247*8ffdff6aSGreg Kroah-Hartman return 0; 248*8ffdff6aSGreg Kroah-Hartman } 249*8ffdff6aSGreg Kroah-Hartman 250*8ffdff6aSGreg Kroah-Hartman static struct comedi_driver daq700_driver = { 251*8ffdff6aSGreg Kroah-Hartman .driver_name = "ni_daq_700", 252*8ffdff6aSGreg Kroah-Hartman .module = THIS_MODULE, 253*8ffdff6aSGreg Kroah-Hartman .auto_attach = daq700_auto_attach, 254*8ffdff6aSGreg Kroah-Hartman .detach = comedi_pcmcia_disable, 255*8ffdff6aSGreg Kroah-Hartman }; 256*8ffdff6aSGreg Kroah-Hartman 257*8ffdff6aSGreg Kroah-Hartman static int daq700_cs_attach(struct pcmcia_device *link) 258*8ffdff6aSGreg Kroah-Hartman { 259*8ffdff6aSGreg Kroah-Hartman return comedi_pcmcia_auto_config(link, &daq700_driver); 260*8ffdff6aSGreg Kroah-Hartman } 261*8ffdff6aSGreg Kroah-Hartman 262*8ffdff6aSGreg Kroah-Hartman static const struct pcmcia_device_id daq700_cs_ids[] = { 263*8ffdff6aSGreg Kroah-Hartman PCMCIA_DEVICE_MANF_CARD(0x010b, 0x4743), 264*8ffdff6aSGreg Kroah-Hartman PCMCIA_DEVICE_NULL 265*8ffdff6aSGreg Kroah-Hartman }; 266*8ffdff6aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(pcmcia, daq700_cs_ids); 267*8ffdff6aSGreg Kroah-Hartman 268*8ffdff6aSGreg Kroah-Hartman static struct pcmcia_driver daq700_cs_driver = { 269*8ffdff6aSGreg Kroah-Hartman .name = "ni_daq_700", 270*8ffdff6aSGreg Kroah-Hartman .owner = THIS_MODULE, 271*8ffdff6aSGreg Kroah-Hartman .id_table = daq700_cs_ids, 272*8ffdff6aSGreg Kroah-Hartman .probe = daq700_cs_attach, 273*8ffdff6aSGreg Kroah-Hartman .remove = comedi_pcmcia_auto_unconfig, 274*8ffdff6aSGreg Kroah-Hartman }; 275*8ffdff6aSGreg Kroah-Hartman module_comedi_pcmcia_driver(daq700_driver, daq700_cs_driver); 276*8ffdff6aSGreg Kroah-Hartman 277*8ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Fred Brooks <nsaspook@nsaspook.com>"); 278*8ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION( 279*8ffdff6aSGreg Kroah-Hartman "Comedi driver for National Instruments PCMCIA DAQCard-700 DIO/AI"); 280*8ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 281