1/* 2 * comedi/drivers/rti800.c 3 * Hardware driver for Analog Devices RTI-800/815 board 4 * 5 * COMEDI - Linux Control and Measurement Device Interface 6 * Copyright (C) 1998 David A. Schleef <ds@schleef.org> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 */ 18 19/* 20 * Driver: rti800 21 * Description: Analog Devices RTI-800/815 22 * Devices: (Analog Devices) RTI-800 [rti800] 23 * (Analog Devices) RTI-815 [rti815] 24 * Author: David A. Schleef <ds@schleef.org> 25 * Status: unknown 26 * Updated: Fri, 05 Sep 2008 14:50:44 +0100 27 * 28 * Configuration options: 29 * [0] - I/O port base address 30 * [1] - IRQ (not supported / unused) 31 * [2] - A/D mux/reference (number of channels) 32 * 0 = differential 33 * 1 = pseudodifferential (common) 34 * 2 = single-ended 35 * [3] - A/D range 36 * 0 = [-10,10] 37 * 1 = [-5,5] 38 * 2 = [0,10] 39 * [4] - A/D encoding 40 * 0 = two's complement 41 * 1 = straight binary 42 * [5] - DAC 0 range 43 * 0 = [-10,10] 44 * 1 = [0,10] 45 * [6] - DAC 0 encoding 46 * 0 = two's complement 47 * 1 = straight binary 48 * [7] - DAC 1 range (same as DAC 0) 49 * [8] - DAC 1 encoding (same as DAC 0) 50 */ 51 52#include <linux/module.h> 53#include <linux/delay.h> 54#include <linux/interrupt.h> 55#include "../comedidev.h" 56 57/* 58 * Register map 59 */ 60#define RTI800_CSR 0x00 61#define RTI800_CSR_BUSY (1 << 7) 62#define RTI800_CSR_DONE (1 << 6) 63#define RTI800_CSR_OVERRUN (1 << 5) 64#define RTI800_CSR_TCR (1 << 4) 65#define RTI800_CSR_DMA_ENAB (1 << 3) 66#define RTI800_CSR_INTR_TC (1 << 2) 67#define RTI800_CSR_INTR_EC (1 << 1) 68#define RTI800_CSR_INTR_OVRN (1 << 0) 69#define RTI800_MUXGAIN 0x01 70#define RTI800_CONVERT 0x02 71#define RTI800_ADCLO 0x03 72#define RTI800_ADCHI 0x04 73#define RTI800_DAC0LO 0x05 74#define RTI800_DAC0HI 0x06 75#define RTI800_DAC1LO 0x07 76#define RTI800_DAC1HI 0x08 77#define RTI800_CLRFLAGS 0x09 78#define RTI800_DI 0x0a 79#define RTI800_DO 0x0b 80#define RTI800_9513A_DATA 0x0c 81#define RTI800_9513A_CNTRL 0x0d 82#define RTI800_9513A_STATUS 0x0d 83 84static const struct comedi_lrange range_rti800_ai_10_bipolar = { 85 4, { 86 BIP_RANGE(10), 87 BIP_RANGE(1), 88 BIP_RANGE(0.1), 89 BIP_RANGE(0.02) 90 } 91}; 92 93static const struct comedi_lrange range_rti800_ai_5_bipolar = { 94 4, { 95 BIP_RANGE(5), 96 BIP_RANGE(0.5), 97 BIP_RANGE(0.05), 98 BIP_RANGE(0.01) 99 } 100}; 101 102static const struct comedi_lrange range_rti800_ai_unipolar = { 103 4, { 104 UNI_RANGE(10), 105 UNI_RANGE(1), 106 UNI_RANGE(0.1), 107 UNI_RANGE(0.02) 108 } 109}; 110 111static const struct comedi_lrange *const rti800_ai_ranges[] = { 112 &range_rti800_ai_10_bipolar, 113 &range_rti800_ai_5_bipolar, 114 &range_rti800_ai_unipolar, 115}; 116 117static const struct comedi_lrange *const rti800_ao_ranges[] = { 118 &range_bipolar10, 119 &range_unipolar10, 120}; 121 122struct rti800_board { 123 const char *name; 124 int has_ao; 125}; 126 127static const struct rti800_board rti800_boardtypes[] = { 128 { 129 .name = "rti800", 130 }, { 131 .name = "rti815", 132 .has_ao = 1, 133 }, 134}; 135 136struct rti800_private { 137 bool adc_2comp; 138 bool dac_2comp[2]; 139 const struct comedi_lrange *ao_range_type_list[2]; 140 unsigned char muxgain_bits; 141}; 142 143static int rti800_ai_eoc(struct comedi_device *dev, 144 struct comedi_subdevice *s, 145 struct comedi_insn *insn, 146 unsigned long context) 147{ 148 unsigned char status; 149 150 status = inb(dev->iobase + RTI800_CSR); 151 if (status & RTI800_CSR_OVERRUN) { 152 outb(0, dev->iobase + RTI800_CLRFLAGS); 153 return -EOVERFLOW; 154 } 155 if (status & RTI800_CSR_DONE) 156 return 0; 157 return -EBUSY; 158} 159 160static int rti800_ai_insn_read(struct comedi_device *dev, 161 struct comedi_subdevice *s, 162 struct comedi_insn *insn, 163 unsigned int *data) 164{ 165 struct rti800_private *devpriv = dev->private; 166 unsigned int chan = CR_CHAN(insn->chanspec); 167 unsigned int gain = CR_RANGE(insn->chanspec); 168 unsigned char muxgain_bits; 169 int ret; 170 int i; 171 172 inb(dev->iobase + RTI800_ADCHI); 173 outb(0, dev->iobase + RTI800_CLRFLAGS); 174 175 muxgain_bits = chan | (gain << 5); 176 if (muxgain_bits != devpriv->muxgain_bits) { 177 devpriv->muxgain_bits = muxgain_bits; 178 outb(devpriv->muxgain_bits, dev->iobase + RTI800_MUXGAIN); 179 /* 180 * Without a delay here, the RTI_CSR_OVERRUN bit 181 * gets set, and you will have an error. 182 */ 183 if (insn->n > 0) { 184 int delay = (gain == 0) ? 10 : 185 (gain == 1) ? 20 : 186 (gain == 2) ? 40 : 80; 187 188 udelay(delay); 189 } 190 } 191 192 for (i = 0; i < insn->n; i++) { 193 outb(0, dev->iobase + RTI800_CONVERT); 194 195 ret = comedi_timeout(dev, s, insn, rti800_ai_eoc, 0); 196 if (ret) 197 return ret; 198 199 data[i] = inb(dev->iobase + RTI800_ADCLO); 200 data[i] |= (inb(dev->iobase + RTI800_ADCHI) & 0xf) << 8; 201 202 if (devpriv->adc_2comp) 203 data[i] ^= 0x800; 204 } 205 206 return insn->n; 207} 208 209static int rti800_ao_insn_write(struct comedi_device *dev, 210 struct comedi_subdevice *s, 211 struct comedi_insn *insn, 212 unsigned int *data) 213{ 214 struct rti800_private *devpriv = dev->private; 215 unsigned int chan = CR_CHAN(insn->chanspec); 216 int reg_lo = chan ? RTI800_DAC1LO : RTI800_DAC0LO; 217 int reg_hi = chan ? RTI800_DAC1HI : RTI800_DAC0HI; 218 int i; 219 220 for (i = 0; i < insn->n; i++) { 221 unsigned int val = data[i]; 222 223 s->readback[chan] = val; 224 225 if (devpriv->dac_2comp[chan]) 226 val ^= 0x800; 227 228 outb(val & 0xff, dev->iobase + reg_lo); 229 outb((val >> 8) & 0xff, dev->iobase + reg_hi); 230 } 231 232 return insn->n; 233} 234 235static int rti800_di_insn_bits(struct comedi_device *dev, 236 struct comedi_subdevice *s, 237 struct comedi_insn *insn, 238 unsigned int *data) 239{ 240 data[1] = inb(dev->iobase + RTI800_DI); 241 return insn->n; 242} 243 244static int rti800_do_insn_bits(struct comedi_device *dev, 245 struct comedi_subdevice *s, 246 struct comedi_insn *insn, 247 unsigned int *data) 248{ 249 if (comedi_dio_update_state(s, data)) { 250 /* Outputs are inverted... */ 251 outb(s->state ^ 0xff, dev->iobase + RTI800_DO); 252 } 253 254 data[1] = s->state; 255 256 return insn->n; 257} 258 259static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it) 260{ 261 const struct rti800_board *board = dev->board_ptr; 262 struct rti800_private *devpriv; 263 struct comedi_subdevice *s; 264 int ret; 265 266 ret = comedi_request_region(dev, it->options[0], 0x10); 267 if (ret) 268 return ret; 269 270 outb(0, dev->iobase + RTI800_CSR); 271 inb(dev->iobase + RTI800_ADCHI); 272 outb(0, dev->iobase + RTI800_CLRFLAGS); 273 274 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 275 if (!devpriv) 276 return -ENOMEM; 277 278 devpriv->adc_2comp = (it->options[4] == 0); 279 devpriv->dac_2comp[0] = (it->options[6] == 0); 280 devpriv->dac_2comp[1] = (it->options[8] == 0); 281 /* invalid, forces the MUXGAIN register to be set when first used */ 282 devpriv->muxgain_bits = 0xff; 283 284 ret = comedi_alloc_subdevices(dev, 4); 285 if (ret) 286 return ret; 287 288 s = &dev->subdevices[0]; 289 /* ai subdevice */ 290 s->type = COMEDI_SUBD_AI; 291 s->subdev_flags = SDF_READABLE | SDF_GROUND; 292 s->n_chan = (it->options[2] ? 16 : 8); 293 s->insn_read = rti800_ai_insn_read; 294 s->maxdata = 0x0fff; 295 s->range_table = (it->options[3] < ARRAY_SIZE(rti800_ai_ranges)) 296 ? rti800_ai_ranges[it->options[3]] 297 : &range_unknown; 298 299 s = &dev->subdevices[1]; 300 if (board->has_ao) { 301 /* ao subdevice (only on rti815) */ 302 s->type = COMEDI_SUBD_AO; 303 s->subdev_flags = SDF_WRITABLE; 304 s->n_chan = 2; 305 s->maxdata = 0x0fff; 306 s->range_table_list = devpriv->ao_range_type_list; 307 devpriv->ao_range_type_list[0] = 308 (it->options[5] < ARRAY_SIZE(rti800_ao_ranges)) 309 ? rti800_ao_ranges[it->options[5]] 310 : &range_unknown; 311 devpriv->ao_range_type_list[1] = 312 (it->options[7] < ARRAY_SIZE(rti800_ao_ranges)) 313 ? rti800_ao_ranges[it->options[7]] 314 : &range_unknown; 315 s->insn_write = rti800_ao_insn_write; 316 s->insn_read = comedi_readback_insn_read; 317 318 ret = comedi_alloc_subdev_readback(s); 319 if (ret) 320 return ret; 321 } else { 322 s->type = COMEDI_SUBD_UNUSED; 323 } 324 325 s = &dev->subdevices[2]; 326 /* di */ 327 s->type = COMEDI_SUBD_DI; 328 s->subdev_flags = SDF_READABLE; 329 s->n_chan = 8; 330 s->insn_bits = rti800_di_insn_bits; 331 s->maxdata = 1; 332 s->range_table = &range_digital; 333 334 s = &dev->subdevices[3]; 335 /* do */ 336 s->type = COMEDI_SUBD_DO; 337 s->subdev_flags = SDF_WRITABLE; 338 s->n_chan = 8; 339 s->insn_bits = rti800_do_insn_bits; 340 s->maxdata = 1; 341 s->range_table = &range_digital; 342 343 /* 344 * There is also an Am9513 timer on these boards. This subdevice 345 * is not currently supported. 346 */ 347 348 return 0; 349} 350 351static struct comedi_driver rti800_driver = { 352 .driver_name = "rti800", 353 .module = THIS_MODULE, 354 .attach = rti800_attach, 355 .detach = comedi_legacy_detach, 356 .num_names = ARRAY_SIZE(rti800_boardtypes), 357 .board_name = &rti800_boardtypes[0].name, 358 .offset = sizeof(struct rti800_board), 359}; 360module_comedi_driver(rti800_driver); 361 362MODULE_DESCRIPTION("Comedi: RTI-800 Multifunction Analog/Digital board"); 363MODULE_AUTHOR("Comedi http://www.comedi.org"); 364MODULE_LICENSE("GPL"); 365