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