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