dt2814.c revision 0707bb04be89b18ee83b5a997e36cc585f0b988d
1/* 2 comedi/drivers/dt2814.c 3 Hardware driver for Data Translation DT2814 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: dt2814 25Description: Data Translation DT2814 26Author: ds 27Status: complete 28Devices: [Data Translation] DT2814 (dt2814) 29 30Configuration options: 31 [0] - I/O port base address 32 [1] - IRQ 33 34This card has 16 analog inputs multiplexed onto a 12 bit ADC. There 35is a minimally useful onboard clock. The base frequency for the 36clock is selected by jumpers, and the clock divider can be selected 37via programmed I/O. Unfortunately, the clock divider can only be 38a power of 10, from 1 to 10^7, of which only 3 or 4 are useful. In 39addition, the clock does not seem to be very accurate. 40*/ 41 42#include "../comedidev.h" 43 44#include <linux/ioport.h> 45#include <linux/delay.h> 46 47#define DT2814_SIZE 2 48 49#define DT2814_CSR 0 50#define DT2814_DATA 1 51 52/* 53 * flags 54 */ 55 56#define DT2814_FINISH 0x80 57#define DT2814_ERR 0x40 58#define DT2814_BUSY 0x20 59#define DT2814_ENB 0x10 60#define DT2814_CHANMASK 0x0f 61 62static int dt2814_attach(struct comedi_device * dev, struct comedi_devconfig * it); 63static int dt2814_detach(struct comedi_device * dev); 64static struct comedi_driver driver_dt2814 = { 65 driver_name:"dt2814", 66 module:THIS_MODULE, 67 attach:dt2814_attach, 68 detach:dt2814_detach, 69}; 70 71COMEDI_INITCLEANUP(driver_dt2814); 72 73static irqreturn_t dt2814_interrupt(int irq, void *dev PT_REGS_ARG); 74 75typedef struct { 76 int ntrig; 77 int curadchan; 78} dt2814_private; 79#define devpriv ((dt2814_private *)dev->private) 80 81#define DT2814_TIMEOUT 10 82#define DT2814_MAX_SPEED 100000 /* Arbitrary 10 khz limit */ 83 84static int dt2814_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, 85 struct comedi_insn * insn, unsigned int * data) 86{ 87 int n, i, hi, lo; 88 int chan; 89 int status = 0; 90 91 for (n = 0; n < insn->n; n++) { 92 chan = CR_CHAN(insn->chanspec); 93 94 outb(chan, dev->iobase + DT2814_CSR); 95 for (i = 0; i < DT2814_TIMEOUT; i++) { 96 status = inb(dev->iobase + DT2814_CSR); 97 printk("dt2814: status: %02x\n", status); 98 comedi_udelay(10); 99 if (status & DT2814_FINISH) 100 break; 101 } 102 if (i >= DT2814_TIMEOUT) { 103 printk("dt2814: status: %02x\n", status); 104 return -ETIMEDOUT; 105 } 106 107 hi = inb(dev->iobase + DT2814_DATA); 108 lo = inb(dev->iobase + DT2814_DATA); 109 110 data[n] = (hi << 4) | (lo >> 4); 111 } 112 113 return n; 114} 115 116static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags) 117{ 118 int i; 119 unsigned int f; 120 121 /* XXX ignores flags */ 122 123 f = 10000; /* ns */ 124 for (i = 0; i < 8; i++) { 125 if ((2 * (*ns)) < (f * 11)) 126 break; 127 f *= 10; 128 } 129 130 *ns = f; 131 132 return i; 133} 134 135static int dt2814_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s, 136 struct comedi_cmd * cmd) 137{ 138 int err = 0; 139 int tmp; 140 141 /* step 1: make sure trigger sources are trivially valid */ 142 143 tmp = cmd->start_src; 144 cmd->start_src &= TRIG_NOW; 145 if (!cmd->start_src || tmp != cmd->start_src) 146 err++; 147 148 tmp = cmd->scan_begin_src; 149 cmd->scan_begin_src &= TRIG_TIMER; 150 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) 151 err++; 152 153 tmp = cmd->convert_src; 154 cmd->convert_src &= TRIG_NOW; 155 if (!cmd->convert_src || tmp != cmd->convert_src) 156 err++; 157 158 tmp = cmd->scan_end_src; 159 cmd->scan_end_src &= TRIG_COUNT; 160 if (!cmd->scan_end_src || tmp != cmd->scan_end_src) 161 err++; 162 163 tmp = cmd->stop_src; 164 cmd->stop_src &= TRIG_COUNT | TRIG_NONE; 165 if (!cmd->stop_src || tmp != cmd->stop_src) 166 err++; 167 168 if (err) 169 return 1; 170 171 /* step 2: make sure trigger sources are unique and mutually compatible */ 172 173 /* note that mutual compatiblity is not an issue here */ 174 if (cmd->stop_src != TRIG_TIMER && cmd->stop_src != TRIG_EXT) 175 err++; 176 177 if (err) 178 return 2; 179 180 /* step 3: make sure arguments are trivially compatible */ 181 182 if (cmd->start_arg != 0) { 183 cmd->start_arg = 0; 184 err++; 185 } 186 if (cmd->scan_begin_arg > 1000000000) { 187 cmd->scan_begin_arg = 1000000000; 188 err++; 189 } 190 if (cmd->scan_begin_arg < DT2814_MAX_SPEED) { 191 cmd->scan_begin_arg = DT2814_MAX_SPEED; 192 err++; 193 } 194 if (cmd->scan_end_arg != cmd->chanlist_len) { 195 cmd->scan_end_arg = cmd->chanlist_len; 196 err++; 197 } 198 if (cmd->stop_src == TRIG_COUNT) { 199 if (cmd->stop_arg < 2) { 200 cmd->stop_arg = 2; 201 err++; 202 } 203 } else { 204 /* TRIG_NONE */ 205 if (cmd->stop_arg != 0) { 206 cmd->stop_arg = 0; 207 err++; 208 } 209 } 210 211 if (err) 212 return 3; 213 214 /* step 4: fix up any arguments */ 215 216 tmp = cmd->scan_begin_arg; 217 dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK); 218 if (tmp != cmd->scan_begin_arg) 219 err++; 220 221 if (err) 222 return 4; 223 224 return 0; 225} 226 227static int dt2814_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s) 228{ 229 struct comedi_cmd *cmd = &s->async->cmd; 230 int chan; 231 int trigvar; 232 233 trigvar = 234 dt2814_ns_to_timer(&cmd->scan_begin_arg, 235 cmd->flags & TRIG_ROUND_MASK); 236 237 chan = CR_CHAN(cmd->chanlist[0]); 238 239 devpriv->ntrig = cmd->stop_arg; 240 outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR); 241 242 return 0; 243 244} 245 246static int dt2814_attach(struct comedi_device * dev, struct comedi_devconfig * it) 247{ 248 int i, irq; 249 int ret; 250 struct comedi_subdevice *s; 251 unsigned long iobase; 252 253 iobase = it->options[0]; 254 printk("comedi%d: dt2814: 0x%04lx ", dev->minor, iobase); 255 if (!request_region(iobase, DT2814_SIZE, "dt2814")) { 256 printk("I/O port conflict\n"); 257 return -EIO; 258 } 259 dev->iobase = iobase; 260 dev->board_name = "dt2814"; 261 262 outb(0, dev->iobase + DT2814_CSR); 263 comedi_udelay(100); 264 if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) { 265 printk("reset error (fatal)\n"); 266 return -EIO; 267 } 268 i = inb(dev->iobase + DT2814_DATA); 269 i = inb(dev->iobase + DT2814_DATA); 270 271 irq = it->options[1]; 272#if 0 273 if (irq < 0) { 274 save_flags(flags); 275 sti(); 276 irqs = probe_irq_on(); 277 278 outb(0, dev->iobase + DT2814_CSR); 279 280 comedi_udelay(100); 281 282 irq = probe_irq_off(irqs); 283 restore_flags(flags); 284 if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) { 285 printk("error probing irq (bad) \n"); 286 } 287 288 i = inb(dev->iobase + DT2814_DATA); 289 i = inb(dev->iobase + DT2814_DATA); 290 } 291#endif 292 dev->irq = 0; 293 if (irq > 0) { 294 if (comedi_request_irq(irq, dt2814_interrupt, 0, "dt2814", dev)) { 295 printk("(irq %d unavailable)\n", irq); 296 } else { 297 printk("( irq = %d )\n", irq); 298 dev->irq = irq; 299 } 300 } else if (irq == 0) { 301 printk("(no irq)\n"); 302 } else { 303#if 0 304 printk("(probe returned multiple irqs--bad)\n"); 305#else 306 printk("(irq probe not implemented)\n"); 307#endif 308 } 309 310 if ((ret = alloc_subdevices(dev, 1)) < 0) 311 return ret; 312 if ((ret = alloc_private(dev, sizeof(dt2814_private))) < 0) 313 return ret; 314 315 s = dev->subdevices + 0; 316 dev->read_subdev = s; 317 s->type = COMEDI_SUBD_AI; 318 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ; 319 s->n_chan = 16; /* XXX */ 320 s->len_chanlist = 1; 321 s->insn_read = dt2814_ai_insn_read; 322 s->do_cmd = dt2814_ai_cmd; 323 s->do_cmdtest = dt2814_ai_cmdtest; 324 s->maxdata = 0xfff; 325 s->range_table = &range_unknown; /* XXX */ 326 327 return 0; 328} 329 330static int dt2814_detach(struct comedi_device * dev) 331{ 332 printk("comedi%d: dt2814: remove\n", dev->minor); 333 334 if (dev->irq) { 335 comedi_free_irq(dev->irq, dev); 336 } 337 if (dev->iobase) { 338 release_region(dev->iobase, DT2814_SIZE); 339 } 340 341 return 0; 342} 343 344static irqreturn_t dt2814_interrupt(int irq, void *d PT_REGS_ARG) 345{ 346 int lo, hi; 347 struct comedi_device *dev = d; 348 struct comedi_subdevice *s; 349 int data; 350 351 if (!dev->attached) { 352 comedi_error(dev, "spurious interrupt"); 353 return IRQ_HANDLED; 354 } 355 356 s = dev->subdevices + 0; 357 358 hi = inb(dev->iobase + DT2814_DATA); 359 lo = inb(dev->iobase + DT2814_DATA); 360 361 data = (hi << 4) | (lo >> 4); 362 363 if (!(--devpriv->ntrig)) { 364 int i; 365 366 outb(0, dev->iobase + DT2814_CSR); 367 /* note: turning off timed mode triggers another 368 sample. */ 369 370 for (i = 0; i < DT2814_TIMEOUT; i++) { 371 if (inb(dev->iobase + DT2814_CSR) & DT2814_FINISH) 372 break; 373 } 374 inb(dev->iobase + DT2814_DATA); 375 inb(dev->iobase + DT2814_DATA); 376 377 s->async->events |= COMEDI_CB_EOA; 378 } 379 comedi_event(dev, s); 380 return IRQ_HANDLED; 381} 382