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 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 22 */ 23/* 24Driver: rti800 25Description: Analog Devices RTI-800/815 26Author: ds 27Status: unknown 28Updated: Fri, 05 Sep 2008 14:50:44 +0100 29Devices: [Analog Devices] RTI-800 (rti800), RTI-815 (rti815) 30 31Configuration options: 32 [0] - I/O port base address 33 [1] - IRQ 34 [2] - A/D reference 35 0 = differential 36 1 = pseudodifferential (common) 37 2 = single-ended 38 [3] - A/D range 39 0 = [-10,10] 40 1 = [-5,5] 41 2 = [0,10] 42 [4] - A/D encoding 43 0 = two's complement 44 1 = straight binary 45 [5] - DAC 0 range 46 0 = [-10,10] 47 1 = [0,10] 48 [6] - DAC 0 encoding 49 0 = two's complement 50 1 = straight binary 51 [7] - DAC 1 range (same as DAC 0) 52 [8] - DAC 1 encoding (same as DAC 0) 53*/ 54 55#include <linux/interrupt.h> 56#include "../comedidev.h" 57 58#include <linux/ioport.h> 59 60#define RTI800_SIZE 16 61 62#define RTI800_CSR 0 63#define RTI800_MUXGAIN 1 64#define RTI800_CONVERT 2 65#define RTI800_ADCLO 3 66#define RTI800_ADCHI 4 67#define RTI800_DAC0LO 5 68#define RTI800_DAC0HI 6 69#define RTI800_DAC1LO 7 70#define RTI800_DAC1HI 8 71#define RTI800_CLRFLAGS 9 72#define RTI800_DI 10 73#define RTI800_DO 11 74#define RTI800_9513A_DATA 12 75#define RTI800_9513A_CNTRL 13 76#define RTI800_9513A_STATUS 13 77 78/* 79 * flags for CSR register 80 */ 81 82#define RTI800_BUSY 0x80 83#define RTI800_DONE 0x40 84#define RTI800_OVERRUN 0x20 85#define RTI800_TCR 0x10 86#define RTI800_DMA_ENAB 0x08 87#define RTI800_INTR_TC 0x04 88#define RTI800_INTR_EC 0x02 89#define RTI800_INTR_OVRN 0x01 90 91#define Am9513_8BITBUS 92 93#define Am9513_output_control(a) outb(a, dev->iobase+RTI800_9513A_CNTRL) 94#define Am9513_output_data(a) outb(a, dev->iobase+RTI800_9513A_DATA) 95#define Am9513_input_data() inb(dev->iobase+RTI800_9513A_DATA) 96#define Am9513_input_status() inb(dev->iobase+RTI800_9513A_STATUS) 97 98#include "am9513.h" 99 100static const struct comedi_lrange range_rti800_ai_10_bipolar = { 4, { 101 BIP_RANGE 102 (10), 103 BIP_RANGE 104 (1), 105 BIP_RANGE 106 (0.1), 107 BIP_RANGE 108 (0.02) 109 } 110}; 111 112static const struct comedi_lrange range_rti800_ai_5_bipolar = { 4, { 113 BIP_RANGE 114 (5), 115 BIP_RANGE 116 (0.5), 117 BIP_RANGE 118 (0.05), 119 BIP_RANGE 120 (0.01) 121 } 122}; 123 124static const struct comedi_lrange range_rti800_ai_unipolar = { 4, { 125 UNI_RANGE 126 (10), 127 UNI_RANGE(1), 128 UNI_RANGE 129 (0.1), 130 UNI_RANGE 131 (0.02) 132 } 133}; 134 135struct rti800_board { 136 137 const char *name; 138 int has_ao; 139}; 140 141static const struct rti800_board boardtypes[] = { 142 {"rti800", 0}, 143 {"rti815", 1}, 144}; 145 146#define this_board ((const struct rti800_board *)dev->board_ptr) 147 148static int rti800_attach(struct comedi_device *dev, 149 struct comedi_devconfig *it); 150static int rti800_detach(struct comedi_device *dev); 151static struct comedi_driver driver_rti800 = { 152 .driver_name = "rti800", 153 .module = THIS_MODULE, 154 .attach = rti800_attach, 155 .detach = rti800_detach, 156 .num_names = ARRAY_SIZE(boardtypes), 157 .board_name = &boardtypes[0].name, 158 .offset = sizeof(struct rti800_board), 159}; 160 161static int __init driver_rti800_init_module(void) 162{ 163 return comedi_driver_register(&driver_rti800); 164} 165 166static void __exit driver_rti800_cleanup_module(void) 167{ 168 comedi_driver_unregister(&driver_rti800); 169} 170 171module_init(driver_rti800_init_module); 172module_exit(driver_rti800_cleanup_module); 173 174static irqreturn_t rti800_interrupt(int irq, void *dev); 175 176struct rti800_private { 177 enum { 178 adc_diff, adc_pseudodiff, adc_singleended 179 } adc_mux; 180 enum { 181 adc_bipolar10, adc_bipolar5, adc_unipolar10 182 } adc_range; 183 enum { 184 adc_2comp, adc_straight 185 } adc_coding; 186 enum { 187 dac_bipolar10, dac_unipolar10 188 } dac0_range, dac1_range; 189 enum { 190 dac_2comp, dac_straight 191 } dac0_coding, dac1_coding; 192 const struct comedi_lrange *ao_range_type_list[2]; 193 unsigned int ao_readback[2]; 194 int muxgain_bits; 195}; 196 197#define devpriv ((struct rti800_private *)dev->private) 198 199#define RTI800_TIMEOUT 100 200 201static irqreturn_t rti800_interrupt(int irq, void *dev) 202{ 203 return IRQ_HANDLED; 204} 205 206/* settling delay times in usec for different gains */ 207static const int gaindelay[] = { 10, 20, 40, 80 }; 208 209static int rti800_ai_insn_read(struct comedi_device *dev, 210 struct comedi_subdevice *s, 211 struct comedi_insn *insn, unsigned int *data) 212{ 213 int i, t; 214 int status; 215 int chan = CR_CHAN(insn->chanspec); 216 unsigned gain = CR_RANGE(insn->chanspec); 217 unsigned muxgain_bits; 218 219 inb(dev->iobase + RTI800_ADCHI); 220 outb(0, dev->iobase + RTI800_CLRFLAGS); 221 222 muxgain_bits = chan | (gain << 5); 223 if (muxgain_bits != devpriv->muxgain_bits) { 224 devpriv->muxgain_bits = muxgain_bits; 225 outb(devpriv->muxgain_bits, dev->iobase + RTI800_MUXGAIN); 226 /* without a delay here, the RTI_OVERRUN bit 227 * gets set, and you will have an error. */ 228 if (insn->n > 0) { 229 BUG_ON(gain >= ARRAY_SIZE(gaindelay)); 230 udelay(gaindelay[gain]); 231 } 232 } 233 234 for (i = 0; i < insn->n; i++) { 235 outb(0, dev->iobase + RTI800_CONVERT); 236 for (t = RTI800_TIMEOUT; t; t--) { 237 status = inb(dev->iobase + RTI800_CSR); 238 if (status & RTI800_OVERRUN) { 239 printk(KERN_WARNING "rti800: a/d overrun\n"); 240 outb(0, dev->iobase + RTI800_CLRFLAGS); 241 return -EIO; 242 } 243 if (status & RTI800_DONE) 244 break; 245 udelay(1); 246 } 247 if (t == 0) { 248 printk(KERN_WARNING "rti800: timeout\n"); 249 return -ETIME; 250 } 251 data[i] = inb(dev->iobase + RTI800_ADCLO); 252 data[i] |= (0xf & inb(dev->iobase + RTI800_ADCHI)) << 8; 253 254 if (devpriv->adc_coding == adc_2comp) 255 data[i] ^= 0x800; 256 } 257 258 return i; 259} 260 261static int rti800_ao_insn_read(struct comedi_device *dev, 262 struct comedi_subdevice *s, 263 struct comedi_insn *insn, unsigned int *data) 264{ 265 int i; 266 int chan = CR_CHAN(insn->chanspec); 267 268 for (i = 0; i < insn->n; i++) 269 data[i] = devpriv->ao_readback[chan]; 270 271 return i; 272} 273 274static int rti800_ao_insn_write(struct comedi_device *dev, 275 struct comedi_subdevice *s, 276 struct comedi_insn *insn, unsigned int *data) 277{ 278 int chan = CR_CHAN(insn->chanspec); 279 int d; 280 int i; 281 282 for (i = 0; i < insn->n; i++) { 283 devpriv->ao_readback[chan] = d = data[i]; 284 if (devpriv->dac0_coding == dac_2comp) 285 d ^= 0x800; 286 287 outb(d & 0xff, 288 dev->iobase + (chan ? RTI800_DAC1LO : RTI800_DAC0LO)); 289 outb(d >> 8, 290 dev->iobase + (chan ? RTI800_DAC1HI : RTI800_DAC0HI)); 291 } 292 return i; 293} 294 295static int rti800_di_insn_bits(struct comedi_device *dev, 296 struct comedi_subdevice *s, 297 struct comedi_insn *insn, unsigned int *data) 298{ 299 if (insn->n != 2) 300 return -EINVAL; 301 data[1] = inb(dev->iobase + RTI800_DI); 302 return 2; 303} 304 305static int rti800_do_insn_bits(struct comedi_device *dev, 306 struct comedi_subdevice *s, 307 struct comedi_insn *insn, unsigned int *data) 308{ 309 if (insn->n != 2) 310 return -EINVAL; 311 312 if (data[0]) { 313 s->state &= ~data[0]; 314 s->state |= data[0] & data[1]; 315 /* Outputs are inverted... */ 316 outb(s->state ^ 0xff, dev->iobase + RTI800_DO); 317 } 318 319 data[1] = s->state; 320 321 return 2; 322} 323 324/* 325 options[0] - I/O port 326 options[1] - irq 327 options[2] - a/d mux 328 0=differential, 1=pseudodiff, 2=single 329 options[3] - a/d range 330 0=bipolar10, 1=bipolar5, 2=unipolar10 331 options[4] - a/d coding 332 0=2's comp, 1=straight binary 333 options[5] - dac0 range 334 0=bipolar10, 1=unipolar10 335 options[6] - dac0 coding 336 0=2's comp, 1=straight binary 337 options[7] - dac1 range 338 options[8] - dac1 coding 339 */ 340 341static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it) 342{ 343 unsigned int irq; 344 unsigned long iobase; 345 int ret; 346 struct comedi_subdevice *s; 347 348 iobase = it->options[0]; 349 printk(KERN_INFO "comedi%d: rti800: 0x%04lx\n", dev->minor, iobase); 350 if (!request_region(iobase, RTI800_SIZE, "rti800")) { 351 printk(KERN_WARNING "I/O port conflict\n"); 352 return -EIO; 353 } 354 dev->iobase = iobase; 355 356#ifdef DEBUG 357 printk(KERN_DEBUG "fingerprint=%x,%x,%x,%x,%x ", 358 inb(dev->iobase + 0), 359 inb(dev->iobase + 1), 360 inb(dev->iobase + 2), 361 inb(dev->iobase + 3), inb(dev->iobase + 4)); 362#endif 363 364 outb(0, dev->iobase + RTI800_CSR); 365 inb(dev->iobase + RTI800_ADCHI); 366 outb(0, dev->iobase + RTI800_CLRFLAGS); 367 368 irq = it->options[1]; 369 if (irq) { 370 printk(KERN_INFO "( irq = %u )\n", irq); 371 ret = request_irq(irq, rti800_interrupt, 0, "rti800", dev); 372 if (ret < 0) { 373 printk(KERN_WARNING " Failed to allocate IRQ\n"); 374 return ret; 375 } 376 dev->irq = irq; 377 } else { 378 printk(KERN_INFO "( no irq )\n"); 379 } 380 381 dev->board_name = this_board->name; 382 383 ret = alloc_subdevices(dev, 4); 384 if (ret < 0) 385 return ret; 386 387 ret = alloc_private(dev, sizeof(struct rti800_private)); 388 if (ret < 0) 389 return ret; 390 391 devpriv->adc_mux = it->options[2]; 392 devpriv->adc_range = it->options[3]; 393 devpriv->adc_coding = it->options[4]; 394 devpriv->dac0_range = it->options[5]; 395 devpriv->dac0_coding = it->options[6]; 396 devpriv->dac1_range = it->options[7]; 397 devpriv->dac1_coding = it->options[8]; 398 devpriv->muxgain_bits = -1; 399 400 s = dev->subdevices + 0; 401 /* ai subdevice */ 402 s->type = COMEDI_SUBD_AI; 403 s->subdev_flags = SDF_READABLE | SDF_GROUND; 404 s->n_chan = (devpriv->adc_mux ? 16 : 8); 405 s->insn_read = rti800_ai_insn_read; 406 s->maxdata = 0xfff; 407 switch (devpriv->adc_range) { 408 case adc_bipolar10: 409 s->range_table = &range_rti800_ai_10_bipolar; 410 break; 411 case adc_bipolar5: 412 s->range_table = &range_rti800_ai_5_bipolar; 413 break; 414 case adc_unipolar10: 415 s->range_table = &range_rti800_ai_unipolar; 416 break; 417 } 418 419 s++; 420 if (this_board->has_ao) { 421 /* ao subdevice (only on rti815) */ 422 s->type = COMEDI_SUBD_AO; 423 s->subdev_flags = SDF_WRITABLE; 424 s->n_chan = 2; 425 s->insn_read = rti800_ao_insn_read; 426 s->insn_write = rti800_ao_insn_write; 427 s->maxdata = 0xfff; 428 s->range_table_list = devpriv->ao_range_type_list; 429 switch (devpriv->dac0_range) { 430 case dac_bipolar10: 431 devpriv->ao_range_type_list[0] = &range_bipolar10; 432 break; 433 case dac_unipolar10: 434 devpriv->ao_range_type_list[0] = &range_unipolar10; 435 break; 436 } 437 switch (devpriv->dac1_range) { 438 case dac_bipolar10: 439 devpriv->ao_range_type_list[1] = &range_bipolar10; 440 break; 441 case dac_unipolar10: 442 devpriv->ao_range_type_list[1] = &range_unipolar10; 443 break; 444 } 445 } else { 446 s->type = COMEDI_SUBD_UNUSED; 447 } 448 449 s++; 450 /* di */ 451 s->type = COMEDI_SUBD_DI; 452 s->subdev_flags = SDF_READABLE; 453 s->n_chan = 8; 454 s->insn_bits = rti800_di_insn_bits; 455 s->maxdata = 1; 456 s->range_table = &range_digital; 457 458 s++; 459 /* do */ 460 s->type = COMEDI_SUBD_DO; 461 s->subdev_flags = SDF_WRITABLE; 462 s->n_chan = 8; 463 s->insn_bits = rti800_do_insn_bits; 464 s->maxdata = 1; 465 s->range_table = &range_digital; 466 467/* don't yet know how to deal with counter/timers */ 468#if 0 469 s++; 470 /* do */ 471 s->type = COMEDI_SUBD_TIMER; 472#endif 473 474 return 0; 475} 476 477static int rti800_detach(struct comedi_device *dev) 478{ 479 printk(KERN_INFO "comedi%d: rti800: remove\n", dev->minor); 480 481 if (dev->iobase) 482 release_region(dev->iobase, RTI800_SIZE); 483 484 if (dev->irq) 485 free_irq(dev->irq, dev); 486 487 return 0; 488} 489 490MODULE_AUTHOR("Comedi http://www.comedi.org"); 491MODULE_DESCRIPTION("Comedi low-level driver"); 492MODULE_LICENSE("GPL"); 493