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