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