dt2814.c revision 68c3dbff9fc9f25872408d0e95980d41733d48d0
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); 74 75struct dt2814_private { 76 77 int ntrig; 78 int curadchan; 79}; 80 81#define devpriv ((struct dt2814_private *)dev->private) 82 83#define DT2814_TIMEOUT 10 84#define DT2814_MAX_SPEED 100000 /* Arbitrary 10 khz limit */ 85 86static int dt2814_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, 87 struct comedi_insn *insn, unsigned int *data) 88{ 89 int n, i, hi, lo; 90 int chan; 91 int status = 0; 92 93 for (n = 0; n < insn->n; n++) { 94 chan = CR_CHAN(insn->chanspec); 95 96 outb(chan, dev->iobase + DT2814_CSR); 97 for (i = 0; i < DT2814_TIMEOUT; i++) { 98 status = inb(dev->iobase + DT2814_CSR); 99 printk("dt2814: status: %02x\n", status); 100 comedi_udelay(10); 101 if (status & DT2814_FINISH) 102 break; 103 } 104 if (i >= DT2814_TIMEOUT) { 105 printk("dt2814: status: %02x\n", status); 106 return -ETIMEDOUT; 107 } 108 109 hi = inb(dev->iobase + DT2814_DATA); 110 lo = inb(dev->iobase + DT2814_DATA); 111 112 data[n] = (hi << 4) | (lo >> 4); 113 } 114 115 return n; 116} 117 118static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags) 119{ 120 int i; 121 unsigned int f; 122 123 /* XXX ignores flags */ 124 125 f = 10000; /* ns */ 126 for (i = 0; i < 8; i++) { 127 if ((2 * (*ns)) < (f * 11)) 128 break; 129 f *= 10; 130 } 131 132 *ns = f; 133 134 return i; 135} 136 137static int dt2814_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, 138 struct comedi_cmd *cmd) 139{ 140 int err = 0; 141 int tmp; 142 143 /* step 1: make sure trigger sources are trivially valid */ 144 145 tmp = cmd->start_src; 146 cmd->start_src &= TRIG_NOW; 147 if (!cmd->start_src || tmp != cmd->start_src) 148 err++; 149 150 tmp = cmd->scan_begin_src; 151 cmd->scan_begin_src &= TRIG_TIMER; 152 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) 153 err++; 154 155 tmp = cmd->convert_src; 156 cmd->convert_src &= TRIG_NOW; 157 if (!cmd->convert_src || tmp != cmd->convert_src) 158 err++; 159 160 tmp = cmd->scan_end_src; 161 cmd->scan_end_src &= TRIG_COUNT; 162 if (!cmd->scan_end_src || tmp != cmd->scan_end_src) 163 err++; 164 165 tmp = cmd->stop_src; 166 cmd->stop_src &= TRIG_COUNT | TRIG_NONE; 167 if (!cmd->stop_src || tmp != cmd->stop_src) 168 err++; 169 170 if (err) 171 return 1; 172 173 /* step 2: make sure trigger sources are unique and mutually compatible */ 174 175 /* note that mutual compatiblity is not an issue here */ 176 if (cmd->stop_src != TRIG_TIMER && cmd->stop_src != TRIG_EXT) 177 err++; 178 179 if (err) 180 return 2; 181 182 /* step 3: make sure arguments are trivially compatible */ 183 184 if (cmd->start_arg != 0) { 185 cmd->start_arg = 0; 186 err++; 187 } 188 if (cmd->scan_begin_arg > 1000000000) { 189 cmd->scan_begin_arg = 1000000000; 190 err++; 191 } 192 if (cmd->scan_begin_arg < DT2814_MAX_SPEED) { 193 cmd->scan_begin_arg = DT2814_MAX_SPEED; 194 err++; 195 } 196 if (cmd->scan_end_arg != cmd->chanlist_len) { 197 cmd->scan_end_arg = cmd->chanlist_len; 198 err++; 199 } 200 if (cmd->stop_src == TRIG_COUNT) { 201 if (cmd->stop_arg < 2) { 202 cmd->stop_arg = 2; 203 err++; 204 } 205 } else { 206 /* TRIG_NONE */ 207 if (cmd->stop_arg != 0) { 208 cmd->stop_arg = 0; 209 err++; 210 } 211 } 212 213 if (err) 214 return 3; 215 216 /* step 4: fix up any arguments */ 217 218 tmp = cmd->scan_begin_arg; 219 dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK); 220 if (tmp != cmd->scan_begin_arg) 221 err++; 222 223 if (err) 224 return 4; 225 226 return 0; 227} 228 229static int dt2814_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 230{ 231 struct comedi_cmd *cmd = &s->async->cmd; 232 int chan; 233 int trigvar; 234 235 trigvar = 236 dt2814_ns_to_timer(&cmd->scan_begin_arg, 237 cmd->flags & TRIG_ROUND_MASK); 238 239 chan = CR_CHAN(cmd->chanlist[0]); 240 241 devpriv->ntrig = cmd->stop_arg; 242 outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR); 243 244 return 0; 245 246} 247 248static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it) 249{ 250 int i, irq; 251 int ret; 252 struct comedi_subdevice *s; 253 unsigned long iobase; 254 255 iobase = it->options[0]; 256 printk("comedi%d: dt2814: 0x%04lx ", dev->minor, iobase); 257 if (!request_region(iobase, DT2814_SIZE, "dt2814")) { 258 printk("I/O port conflict\n"); 259 return -EIO; 260 } 261 dev->iobase = iobase; 262 dev->board_name = "dt2814"; 263 264 outb(0, dev->iobase + DT2814_CSR); 265 comedi_udelay(100); 266 if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) { 267 printk("reset error (fatal)\n"); 268 return -EIO; 269 } 270 i = inb(dev->iobase + DT2814_DATA); 271 i = inb(dev->iobase + DT2814_DATA); 272 273 irq = it->options[1]; 274#if 0 275 if (irq < 0) { 276 save_flags(flags); 277 sti(); 278 irqs = probe_irq_on(); 279 280 outb(0, dev->iobase + DT2814_CSR); 281 282 comedi_udelay(100); 283 284 irq = probe_irq_off(irqs); 285 restore_flags(flags); 286 if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) { 287 printk("error probing irq (bad) \n"); 288 } 289 290 i = inb(dev->iobase + DT2814_DATA); 291 i = inb(dev->iobase + DT2814_DATA); 292 } 293#endif 294 dev->irq = 0; 295 if (irq > 0) { 296 if (comedi_request_irq(irq, dt2814_interrupt, 0, "dt2814", dev)) { 297 printk("(irq %d unavailable)\n", irq); 298 } else { 299 printk("( irq = %d )\n", irq); 300 dev->irq = irq; 301 } 302 } else if (irq == 0) { 303 printk("(no irq)\n"); 304 } else { 305#if 0 306 printk("(probe returned multiple irqs--bad)\n"); 307#else 308 printk("(irq probe not implemented)\n"); 309#endif 310 } 311 312 ret = alloc_subdevices(dev, 1); 313 if (ret < 0) 314 return ret; 315 316 ret = alloc_private(dev, sizeof(struct dt2814_private)); 317 if (ret < 0) 318 return ret; 319 320 s = dev->subdevices + 0; 321 dev->read_subdev = s; 322 s->type = COMEDI_SUBD_AI; 323 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ; 324 s->n_chan = 16; /* XXX */ 325 s->len_chanlist = 1; 326 s->insn_read = dt2814_ai_insn_read; 327 s->do_cmd = dt2814_ai_cmd; 328 s->do_cmdtest = dt2814_ai_cmdtest; 329 s->maxdata = 0xfff; 330 s->range_table = &range_unknown; /* XXX */ 331 332 return 0; 333} 334 335static int dt2814_detach(struct comedi_device *dev) 336{ 337 printk("comedi%d: dt2814: remove\n", dev->minor); 338 339 if (dev->irq) { 340 comedi_free_irq(dev->irq, dev); 341 } 342 if (dev->iobase) { 343 release_region(dev->iobase, DT2814_SIZE); 344 } 345 346 return 0; 347} 348 349static irqreturn_t dt2814_interrupt(int irq, void *d) 350{ 351 int lo, hi; 352 struct comedi_device *dev = d; 353 struct comedi_subdevice *s; 354 int data; 355 356 if (!dev->attached) { 357 comedi_error(dev, "spurious interrupt"); 358 return IRQ_HANDLED; 359 } 360 361 s = dev->subdevices + 0; 362 363 hi = inb(dev->iobase + DT2814_DATA); 364 lo = inb(dev->iobase + DT2814_DATA); 365 366 data = (hi << 4) | (lo >> 4); 367 368 if (!(--devpriv->ntrig)) { 369 int i; 370 371 outb(0, dev->iobase + DT2814_CSR); 372 /* note: turning off timed mode triggers another 373 sample. */ 374 375 for (i = 0; i < DT2814_TIMEOUT; i++) { 376 if (inb(dev->iobase + DT2814_CSR) & DT2814_FINISH) 377 break; 378 } 379 inb(dev->iobase + DT2814_DATA); 380 inb(dev->iobase + DT2814_DATA); 381 382 s->async->events |= COMEDI_CB_EOA; 383 } 384 comedi_event(dev, s); 385 return IRQ_HANDLED; 386} 387