das6402.c revision 7e4198f47bda09b15d06822b610f99065901e45c
1/* 2 Some comments on the code.. 3 4 - it shouldn't be necessary to use outb_p(). 5 6 - ignoreirq creates a race condition. It needs to be fixed. 7 8 */ 9 10/* 11 comedi/drivers/das6402.c 12 An experimental driver for Computerboards' DAS6402 I/O card 13 14 Copyright (C) 1999 Oystein Svendsen <svendsen@pvv.org> 15 16 This program is free software; you can redistribute it and/or modify 17 it under the terms of the GNU General Public License as published by 18 the Free Software Foundation; either version 2 of the License, or 19 (at your option) any later version. 20 21 This program is distributed in the hope that it will be useful, 22 but WITHOUT ANY WARRANTY; without even the implied warranty of 23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 GNU General Public License for more details. 25 26 You should have received a copy of the GNU General Public License 27 along with this program; if not, write to the Free Software 28 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 29 30 */ 31/* 32Driver: das6402 33Description: Keithley Metrabyte DAS6402 (& compatibles) 34Author: Oystein Svendsen <svendsen@pvv.org> 35Status: bitrotten 36Devices: [Keithley Metrabyte] DAS6402 (das6402) 37 38This driver has suffered bitrot. 39*/ 40 41#include <linux/interrupt.h> 42#include "../comedidev.h" 43 44#include <linux/ioport.h> 45 46#define DAS6402_SIZE 16 47 48#define N_WORDS (3000*64) 49 50#define STOP 0 51#define START 1 52 53#define SCANL 0x3f00 54#define BYTE unsigned char 55#define WORD unsigned short 56 57/*----- register 8 ----*/ 58#define CLRINT 0x01 59#define CLRXTR 0x02 60#define CLRXIN 0x04 61#define EXTEND 0x10 62#define ARMED 0x20 /* enable conting of post sample conv */ 63#define POSTMODE 0x40 64#define MHZ 0x80 /* 10 MHz clock */ 65/*---------------------*/ 66 67/*----- register 9 ----*/ 68#define IRQ (0x04 << 4) /* these two are */ 69#define IRQV 10 /* dependent on each other */ 70 71#define CONVSRC 0x03 /* trig src is Intarnal pacer */ 72#define BURSTEN 0x04 /* enable burst */ 73#define XINTE 0x08 /* use external int. trig */ 74#define INTE 0x80 /* enable analog interrupts */ 75/*---------------------*/ 76 77/*----- register 10 ---*/ 78#define TGEN 0x01 /* Use pin DI1 for externl trigging? */ 79#define TGSEL 0x02 /* Use edge triggering */ 80#define TGPOL 0x04 /* active edge is falling */ 81#define PRETRIG 0x08 /* pretrig */ 82/*---------------------*/ 83 84/*----- register 11 ---*/ 85#define EOB 0x0c 86#define FIFOHFULL 0x08 87#define GAIN 0x01 88#define FIFONEPTY 0x04 89#define MODE 0x10 90#define SEM 0x20 91#define BIP 0x40 92/*---------------------*/ 93 94#define M0 0x00 95#define M2 0x04 96 97#define C0 0x00 98#define C1 0x40 99#define C2 0x80 100#define RWLH 0x30 101 102static int das6402_attach(struct comedi_device *dev, 103 struct comedi_devconfig *it); 104static int das6402_detach(struct comedi_device *dev); 105static struct comedi_driver driver_das6402 = { 106 .driver_name = "das6402", 107 .module = THIS_MODULE, 108 .attach = das6402_attach, 109 .detach = das6402_detach, 110}; 111 112static int __init driver_das6402_init_module(void) 113{ 114 return comedi_driver_register(&driver_das6402); 115} 116 117static void __exit driver_das6402_cleanup_module(void) 118{ 119 comedi_driver_unregister(&driver_das6402); 120} 121 122module_init(driver_das6402_init_module); 123module_exit(driver_das6402_cleanup_module); 124 125struct das6402_private { 126 int ai_bytes_to_read; 127 128 int das6402_ignoreirq; 129}; 130#define devpriv ((struct das6402_private *)dev->private) 131 132static void das6402_ai_fifo_dregs(struct comedi_device *dev, 133 struct comedi_subdevice *s); 134 135static void das6402_setcounter(struct comedi_device *dev) 136{ 137 BYTE p; 138 unsigned short ctrlwrd; 139 140 /* set up counter0 first, mode 0 */ 141 p = M0 | C0 | RWLH; 142 outb_p(p, dev->iobase + 15); 143 ctrlwrd = 2000; 144 p = (BYTE) (0xff & ctrlwrd); 145 outb_p(p, dev->iobase + 12); 146 p = (BYTE) (0xff & (ctrlwrd >> 8)); 147 outb_p(p, dev->iobase + 12); 148 149 /* set up counter1, mode 2 */ 150 p = M2 | C1 | RWLH; 151 outb_p(p, dev->iobase + 15); 152 ctrlwrd = 10; 153 p = (BYTE) (0xff & ctrlwrd); 154 outb_p(p, dev->iobase + 13); 155 p = (BYTE) (0xff & (ctrlwrd >> 8)); 156 outb_p(p, dev->iobase + 13); 157 158 /* set up counter1, mode 2 */ 159 p = M2 | C2 | RWLH; 160 outb_p(p, dev->iobase + 15); 161 ctrlwrd = 1000; 162 p = (BYTE) (0xff & ctrlwrd); 163 outb_p(p, dev->iobase + 14); 164 p = (BYTE) (0xff & (ctrlwrd >> 8)); 165 outb_p(p, dev->iobase + 14); 166} 167 168static irqreturn_t intr_handler(int irq, void *d) 169{ 170 struct comedi_device *dev = d; 171 struct comedi_subdevice *s = dev->subdevices; 172 173 if (!dev->attached || devpriv->das6402_ignoreirq) { 174 dev_warn(dev->hw_dev, "BUG: spurious interrupt\n"); 175 return IRQ_HANDLED; 176 } 177#ifdef DEBUG 178 printk("das6402: interrupt! das6402_irqcount=%i\n", 179 devpriv->das6402_irqcount); 180 printk("das6402: iobase+2=%i\n", inw_p(dev->iobase + 2)); 181#endif 182 183 das6402_ai_fifo_dregs(dev, s); 184 185 if (s->async->buf_write_count >= devpriv->ai_bytes_to_read) { 186 outw_p(SCANL, dev->iobase + 2); /* clears the fifo */ 187 outb(0x07, dev->iobase + 8); /* clears all flip-flops */ 188#ifdef DEBUG 189 printk("das6402: Got %i samples\n\n", 190 devpriv->das6402_wordsread - diff); 191#endif 192 s->async->events |= COMEDI_CB_EOA; 193 comedi_event(dev, s); 194 } 195 196 outb(0x01, dev->iobase + 8); /* clear only the interrupt flip-flop */ 197 198 comedi_event(dev, s); 199 return IRQ_HANDLED; 200} 201 202#if 0 203static void das6402_ai_fifo_read(struct comedi_device *dev, short *data, int n) 204{ 205 int i; 206 207 for (i = 0; i < n; i++) 208 data[i] = inw(dev->iobase); 209} 210#endif 211 212static void das6402_ai_fifo_dregs(struct comedi_device *dev, 213 struct comedi_subdevice *s) 214{ 215 while (1) { 216 if (!(inb(dev->iobase + 8) & 0x01)) 217 return; 218 comedi_buf_put(s->async, inw(dev->iobase)); 219 } 220} 221 222static int das6402_ai_cancel(struct comedi_device *dev, 223 struct comedi_subdevice *s) 224{ 225 /* 226 * This function should reset the board from whatever condition it 227 * is in (i.e., acquiring data), to a non-active state. 228 */ 229 230 devpriv->das6402_ignoreirq = 1; 231 dev_dbg(dev->hw_dev, "Stopping acquisition\n"); 232 devpriv->das6402_ignoreirq = 1; 233 outb_p(0x02, dev->iobase + 10); /* disable external trigging */ 234 outw_p(SCANL, dev->iobase + 2); /* resets the card fifo */ 235 outb_p(0, dev->iobase + 9); /* disables interrupts */ 236 237 outw_p(SCANL, dev->iobase + 2); 238 239 return 0; 240} 241 242#ifdef unused 243static int das6402_ai_mode2(struct comedi_device *dev, 244 struct comedi_subdevice *s, comedi_trig * it) 245{ 246 devpriv->das6402_ignoreirq = 1; 247 dev_dbg(dev->hw_dev, "Starting acquisition\n"); 248 outb_p(0x03, dev->iobase + 10); /* enable external trigging */ 249 outw_p(SCANL, dev->iobase + 2); /* resets the card fifo */ 250 outb_p(IRQ | CONVSRC | BURSTEN | INTE, dev->iobase + 9); 251 252 devpriv->ai_bytes_to_read = it->n * sizeof(short); 253 254 /* um... ignoreirq is a nasty race condition */ 255 devpriv->das6402_ignoreirq = 0; 256 257 outw_p(SCANL, dev->iobase + 2); 258 259 return 0; 260} 261#endif 262 263static int board_init(struct comedi_device *dev) 264{ 265 BYTE b; 266 267 devpriv->das6402_ignoreirq = 1; 268 269 outb(0x07, dev->iobase + 8); 270 271 /* register 11 */ 272 outb_p(MODE, dev->iobase + 11); 273 b = BIP | SEM | MODE | GAIN | FIFOHFULL; 274 outb_p(b, dev->iobase + 11); 275 276 /* register 8 */ 277 outb_p(EXTEND, dev->iobase + 8); 278 b = EXTEND | MHZ; 279 outb_p(b, dev->iobase + 8); 280 b = MHZ | CLRINT | CLRXTR | CLRXIN; 281 outb_p(b, dev->iobase + 8); 282 283 /* register 9 */ 284 b = IRQ | CONVSRC | BURSTEN | INTE; 285 outb_p(b, dev->iobase + 9); 286 287 /* register 10 */ 288 b = TGSEL | TGEN; 289 outb_p(b, dev->iobase + 10); 290 291 b = 0x07; 292 outb_p(b, dev->iobase + 8); 293 294 das6402_setcounter(dev); 295 296 outw_p(SCANL, dev->iobase + 2); /* reset card fifo */ 297 298 devpriv->das6402_ignoreirq = 0; 299 300 return 0; 301} 302 303static int das6402_detach(struct comedi_device *dev) 304{ 305 if (dev->irq) 306 free_irq(dev->irq, dev); 307 if (dev->iobase) 308 release_region(dev->iobase, DAS6402_SIZE); 309 310 return 0; 311} 312 313static int das6402_attach(struct comedi_device *dev, 314 struct comedi_devconfig *it) 315{ 316 unsigned int irq; 317 unsigned long iobase; 318 int ret; 319 struct comedi_subdevice *s; 320 321 dev->board_name = "das6402"; 322 323 iobase = it->options[0]; 324 if (iobase == 0) 325 iobase = 0x300; 326 327 if (!request_region(iobase, DAS6402_SIZE, "das6402")) { 328 dev_err(dev->hw_dev, "I/O port conflict\n"); 329 return -EIO; 330 } 331 dev->iobase = iobase; 332 333 /* should do a probe here */ 334 335 irq = it->options[0]; 336 dev_dbg(dev->hw_dev, "( irq = %u )\n", irq); 337 ret = request_irq(irq, intr_handler, 0, "das6402", dev); 338 if (ret < 0) 339 return ret; 340 341 dev->irq = irq; 342 ret = alloc_private(dev, sizeof(struct das6402_private)); 343 if (ret < 0) 344 return ret; 345 346 ret = alloc_subdevices(dev, 1); 347 if (ret < 0) 348 return ret; 349 350 /* ai subdevice */ 351 s = dev->subdevices + 0; 352 s->type = COMEDI_SUBD_AI; 353 s->subdev_flags = SDF_READABLE | SDF_GROUND; 354 s->n_chan = 8; 355 /* s->trig[2]=das6402_ai_mode2; */ 356 s->cancel = das6402_ai_cancel; 357 s->maxdata = (1 << 12) - 1; 358 s->len_chanlist = 16; /* ? */ 359 s->range_table = &range_unknown; 360 361 board_init(dev); 362 363 return 0; 364} 365 366MODULE_AUTHOR("Comedi http://www.comedi.org"); 367MODULE_DESCRIPTION("Comedi low-level driver"); 368MODULE_LICENSE("GPL"); 369