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