blob: 6ed19e21c9a405f7fd1f171100b4c0f50d2c432f [file] [log] [blame]
Juan Grigeradd2996b2009-02-19 09:30:57 -08001/*
2 comedi/drivers/pcl816.c
3
4 Author: Juan Grigera <juan@grigera.com.ar>
5 based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
6
7 hardware driver for Advantech cards:
8 card: PCL-816, PCL814B
9 driver: pcl816
10*/
11/*
12Driver: pcl816
13Description: Advantech PCL-816 cards, PCL-814
14Author: Juan Grigera <juan@grigera.com.ar>
15Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
16Status: works
17Updated: Tue, 2 Apr 2002 23:15:21 -0800
18
19PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
20Differences are at resolution (16 vs 12 bits).
21
22The driver support AI command mode, other subdevices not written.
23
24Analog output and digital input and output are not supported.
25
26Configuration Options:
27 [0] - IO Base
28 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
29 [2] - DMA (0=disable, 1, 3)
30 [3] - 0, 10=10MHz clock for 8254
31 1= 1MHz clock for 8254
32
33*/
34
35#include "../comedidev.h"
36
37#include <linux/ioport.h>
38#include <linux/mc146818rtc.h>
39#include <linux/delay.h>
40#include <asm/dma.h>
41
42#include "8253.h"
43
44#define DEBUG(x) x
45
46// boards constants
47// IO space len
48#define PCLx1x_RANGE 16
49
50//#define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y)
51
52// INTEL 8254 counters
53#define PCL816_CTR0 4
54#define PCL816_CTR1 5
55#define PCL816_CTR2 6
56// R: counter read-back register W: counter control
57#define PCL816_CTRCTL 7
58
59// R: A/D high byte W: A/D range control
60#define PCL816_RANGE 9
61// W: clear INT request
62#define PCL816_CLRINT 10
63// R: next mux scan channel W: mux scan channel & range control pointer
64#define PCL816_MUX 11
65// R/W: operation control register
66#define PCL816_CONTROL 12
67
68// R: return status byte W: set DMA/IRQ
69#define PCL816_STATUS 13
70#define PCL816_STATUS_DRDY_MASK 0x80
71
72// R: low byte of A/D W: soft A/D trigger
73#define PCL816_AD_LO 8
74// R: high byte of A/D W: A/D range control
75#define PCL816_AD_HI 9
76
77// type of interrupt handler
78#define INT_TYPE_AI1_INT 1
79#define INT_TYPE_AI1_DMA 2
80#define INT_TYPE_AI3_INT 4
81#define INT_TYPE_AI3_DMA 5
82#ifdef unused
83#define INT_TYPE_AI1_DMA_RTC 9
84#define INT_TYPE_AI3_DMA_RTC 10
85
86// RTC stuff...
87#define RTC_IRQ 8
88#define RTC_IO_EXTENT 0x10
89#endif
90
91#define MAGIC_DMA_WORD 0x5a5a
92
Bill Pemberton9ced1de2009-03-16 22:05:31 -040093static const struct comedi_lrange range_pcl816 = { 8, {
Juan Grigeradd2996b2009-02-19 09:30:57 -080094 BIP_RANGE(10),
95 BIP_RANGE(5),
96 BIP_RANGE(2.5),
97 BIP_RANGE(1.25),
98 UNI_RANGE(10),
99 UNI_RANGE(5),
100 UNI_RANGE(2.5),
101 UNI_RANGE(1.25),
102 }
103};
104typedef struct {
105 const char *name; // board name
106 int n_ranges; // len of range list
107 int n_aichan; // num of A/D chans in diferencial mode
108 unsigned int ai_ns_min; // minimal alllowed delay between samples (in ns)
109 int n_aochan; // num of D/A chans
110 int n_dichan; // num of DI chans
111 int n_dochan; // num of DO chans
Bill Pemberton9ced1de2009-03-16 22:05:31 -0400112 const struct comedi_lrange *ai_range_type; // default A/D rangelist
113 const struct comedi_lrange *ao_range_type; // dafault D/A rangelist
Juan Grigeradd2996b2009-02-19 09:30:57 -0800114 unsigned int io_range; // len of IO space
115 unsigned int IRQbits; // allowed interrupts
116 unsigned int DMAbits; // allowed DMA chans
117 int ai_maxdata; // maxdata for A/D
118 int ao_maxdata; // maxdata for D/A
119 int ai_chanlist; // allowed len of channel list A/D
120 int ao_chanlist; // allowed len of channel list D/A
121 int i8254_osc_base; // 1/frequency of on board oscilator in ns
122} boardtype;
123
124static const boardtype boardtypes[] = {
125 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
126 &range_pcl816, PCLx1x_RANGE,
127 0x00fc, // IRQ mask
128 0x0a, // DMA mask
129 0xffff, // 16-bit card
130 0xffff, // D/A maxdata
131 1024,
132 1, // ao chan list
133 100},
134 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
135 &range_pcl816, PCLx1x_RANGE,
136 0x00fc,
137 0x0a,
138 0x3fff, /* 14 bit card */
139 0x3fff,
140 1024,
141 1,
142 100},
143};
144
145#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
Bill Pembertonfe0ff172009-03-16 22:18:43 -0400146#define devpriv ((struct pcl816_private *)dev->private)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800147#define this_board ((const boardtype *)dev->board_ptr)
148
Bill Pemberton0707bb02009-03-16 22:06:20 -0400149static int pcl816_attach(struct comedi_device * dev, struct comedi_devconfig * it);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400150static int pcl816_detach(struct comedi_device * dev);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800151
152#ifdef unused
153static int RTC_lock = 0; /* RTC lock */
154static int RTC_timer_lock = 0; /* RTC int lock */
155#endif
156
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400157static struct comedi_driver driver_pcl816 = {
Juan Grigeradd2996b2009-02-19 09:30:57 -0800158 driver_name:"pcl816",
159 module:THIS_MODULE,
160 attach:pcl816_attach,
161 detach:pcl816_detach,
162 board_name:&boardtypes[0].name,
163 num_names:n_boardtypes,
164 offset:sizeof(boardtype),
165};
166
167COMEDI_INITCLEANUP(driver_pcl816);
168
Bill Pembertonfe0ff172009-03-16 22:18:43 -0400169struct pcl816_private {
170
Juan Grigeradd2996b2009-02-19 09:30:57 -0800171 unsigned int dma; // used DMA, 0=don't use DMA
172 int dma_rtc; // 1=RTC used with DMA, 0=no RTC alloc
173#ifdef unused
174 unsigned long rtc_iobase; // RTC port region
175 unsigned int rtc_iosize;
176 unsigned int rtc_irq;
177#endif
178 unsigned long dmabuf[2]; // pointers to begin of DMA buffers
179 unsigned int dmapages[2]; // len of DMA buffers in PAGE_SIZEs
180 unsigned int hwdmaptr[2]; // hardware address of DMA buffers
181 unsigned int hwdmasize[2]; // len of DMA buffers in Bytes
182 unsigned int dmasamplsize; // size in samples hwdmasize[0]/2
183 unsigned int last_top_dma; // DMA pointer in last RTC int
184 int next_dma_buf; // which DMA buffer will be used next round
185 long dma_runs_to_end; // how many we must permorm DMA transfer to end of record
186 unsigned long last_dma_run; // how many bytes we must transfer on last DMA page
187
188 unsigned int ai_scans; // len of scanlist
189 unsigned char ai_neverending; // if=1, then we do neverending record (you must use cancel())
190 int irq_free; // 1=have allocated IRQ
191 int irq_blocked; // 1=IRQ now uses any subdev
192#ifdef unused
193 int rtc_irq_blocked; // 1=we now do AI with DMA&RTC
194#endif
195 int irq_was_now_closed; // when IRQ finish, there's stored int816_mode for last interrupt
196 int int816_mode; // who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma
Bill Pemberton34c43922009-03-16 22:05:14 -0400197 struct comedi_subdevice *last_int_sub; // ptr to subdevice which now finish
Juan Grigeradd2996b2009-02-19 09:30:57 -0800198 int ai_act_scan; // how many scans we finished
199 unsigned int ai_act_chanlist[16]; // MUX setting for actual AI operations
200 unsigned int ai_act_chanlist_len; // how long is actual MUX list
201 unsigned int ai_act_chanlist_pos; // actual position in MUX list
202 unsigned int ai_poll_ptr; // how many sampes transfer poll
Bill Pemberton34c43922009-03-16 22:05:14 -0400203 struct comedi_subdevice *sub_ai; // ptr to AI subdevice
Juan Grigeradd2996b2009-02-19 09:30:57 -0800204#ifdef unused
205 struct timer_list rtc_irq_timer; // timer for RTC sanity check
206 unsigned long rtc_freq; // RTC int freq
207#endif
Bill Pembertonfe0ff172009-03-16 22:18:43 -0400208};
209
Juan Grigeradd2996b2009-02-19 09:30:57 -0800210
211/*
212==============================================================================
213*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400214static int check_and_setup_channel_list(struct comedi_device * dev,
Bill Pemberton34c43922009-03-16 22:05:14 -0400215 struct comedi_subdevice * s, unsigned int *chanlist, int chanlen);
216static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400217static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
Juan Grigeradd2996b2009-02-19 09:30:57 -0800218 unsigned int divisor2);
219#ifdef unused
220static int set_rtc_irq_bit(unsigned char bit);
221#endif
222
Bill Pemberton34c43922009-03-16 22:05:14 -0400223static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400224 struct comedi_cmd * cmd);
Bill Pemberton34c43922009-03-16 22:05:14 -0400225static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800226
227/*
228==============================================================================
229 ANALOG INPUT MODE0, 816 cards, slow version
230*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400231static int pcl816_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
Bill Pemberton90035c02009-03-16 22:05:53 -0400232 struct comedi_insn * insn, unsigned int * data)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800233{
234 int n;
235 int timeout;
236
237 DPRINTK("mode 0 analog input\n");
238 // software trigger, DMA and INT off
239 outb(0, dev->iobase + PCL816_CONTROL);
240 // clear INT (conversion end) flag
241 outb(0, dev->iobase + PCL816_CLRINT);
242
243 // Set the input channel
244 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
245 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */
246
247 for (n = 0; n < insn->n; n++) {
248
249 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
250
251 timeout = 100;
252 while (timeout--) {
253 if (!(inb(dev->iobase + PCL816_STATUS) &
254 PCL816_STATUS_DRDY_MASK)) {
255 // return read value
256 data[n] =
257 ((inb(dev->iobase +
258 PCL816_AD_HI) << 8) |
259 (inb(dev->iobase + PCL816_AD_LO)));
260
261 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
262 break;
263 }
264 comedi_udelay(1);
265 }
266 // Return timeout error
267 if (!timeout) {
268 comedi_error(dev, "A/D insn timeout\n");
269 data[0] = 0;
270 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
271 return -EIO;
272 }
273
274 }
275 return n;
276}
277
278/*
279==============================================================================
280 analog input interrupt mode 1 & 3, 818 cards
281 one sample per interrupt version
282*/
283static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
284{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400285 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400286 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800287 int low, hi;
288 int timeout = 50; /* wait max 50us */
289
290 while (timeout--) {
291 if (!(inb(dev->iobase + PCL816_STATUS) &
292 PCL816_STATUS_DRDY_MASK))
293 break;
294 comedi_udelay(1);
295 }
296 if (!timeout) { // timeout, bail error
297 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
298 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
299 pcl816_ai_cancel(dev, s);
300 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
301 comedi_event(dev, s);
302 return IRQ_HANDLED;
303
304 }
305
306 // get the sample
307 low = inb(dev->iobase + PCL816_AD_LO);
308 hi = inb(dev->iobase + PCL816_AD_HI);
309
310 comedi_buf_put(s->async, (hi << 8) | low);
311
312 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
313
314 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
315 devpriv->ai_act_chanlist_pos = 0;
316
317 if (s->async->cur_chan == 0) {
318 devpriv->ai_act_scan++;
319 }
320
321 if (!devpriv->ai_neverending)
322 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
323 /* all data sampled */
324 pcl816_ai_cancel(dev, s);
325 s->async->events |= COMEDI_CB_EOA;
326 }
327 comedi_event(dev, s);
328 return IRQ_HANDLED;
329}
330
331/*
332==============================================================================
333 analog input dma mode 1 & 3, 816 cards
334*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400335static void transfer_from_dma_buf(struct comedi_device * dev, struct comedi_subdevice * s,
Bill Pemberton790c5542009-03-16 22:05:02 -0400336 short * ptr, unsigned int bufptr, unsigned int len)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800337{
338 int i;
339
340 s->async->events = 0;
341
342 for (i = 0; i < len; i++) {
343
344 comedi_buf_put(s->async, ptr[bufptr++]);
345
346 if (++devpriv->ai_act_chanlist_pos >=
347 devpriv->ai_act_chanlist_len) {
348 devpriv->ai_act_chanlist_pos = 0;
349 devpriv->ai_act_scan++;
350 }
351
352 if (!devpriv->ai_neverending)
353 if (devpriv->ai_act_scan >= devpriv->ai_scans) { // all data sampled
354 pcl816_ai_cancel(dev, s);
355 s->async->events |= COMEDI_CB_EOA;
356 s->async->events |= COMEDI_CB_BLOCK;
357 break;
358 }
359 }
360
361 comedi_event(dev, s);
362}
363
364static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
365{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400366 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400367 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800368 int len, bufptr, this_dma_buf;
369 unsigned long dma_flags;
Bill Pemberton790c5542009-03-16 22:05:02 -0400370 short *ptr;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800371
372 disable_dma(devpriv->dma);
373 this_dma_buf = devpriv->next_dma_buf;
374
375 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { // switch dma bufs
376
377 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
378 set_dma_mode(devpriv->dma, DMA_MODE_READ);
379 dma_flags = claim_dma_lock();
380// clear_dma_ff (devpriv->dma);
381 set_dma_addr(devpriv->dma,
382 devpriv->hwdmaptr[devpriv->next_dma_buf]);
383 if (devpriv->dma_runs_to_end) {
384 set_dma_count(devpriv->dma,
385 devpriv->hwdmasize[devpriv->next_dma_buf]);
386 } else {
387 set_dma_count(devpriv->dma, devpriv->last_dma_run);
388 }
389 release_dma_lock(dma_flags);
390 enable_dma(devpriv->dma);
391 }
392
393 devpriv->dma_runs_to_end--;
394 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
395
Bill Pemberton790c5542009-03-16 22:05:02 -0400396 ptr = (short *) devpriv->dmabuf[this_dma_buf];
Juan Grigeradd2996b2009-02-19 09:30:57 -0800397
398 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
399 bufptr = devpriv->ai_poll_ptr;
400 devpriv->ai_poll_ptr = 0;
401
402 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
403 return IRQ_HANDLED;
404}
405
406/*
407==============================================================================
408 INT procedure
409*/
410static irqreturn_t interrupt_pcl816(int irq, void *d PT_REGS_ARG)
411{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400412 struct comedi_device *dev = d;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800413 DPRINTK("<I>");
414
415 if (!dev->attached) {
416 comedi_error(dev, "premature interrupt");
417 return IRQ_HANDLED;
418 }
419
420 switch (devpriv->int816_mode) {
421 case INT_TYPE_AI1_DMA:
422 case INT_TYPE_AI3_DMA:
423 return interrupt_pcl816_ai_mode13_dma(irq, d);
424 case INT_TYPE_AI1_INT:
425 case INT_TYPE_AI3_INT:
426 return interrupt_pcl816_ai_mode13_int(irq, d);
427 }
428
429 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
430 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
431 (!devpriv->int816_mode)) {
432 if (devpriv->irq_was_now_closed) {
433 devpriv->irq_was_now_closed = 0;
434 // comedi_error(dev,"last IRQ..");
435 return IRQ_HANDLED;
436 }
437 comedi_error(dev, "bad IRQ!");
438 return IRQ_NONE;
439 }
440 comedi_error(dev, "IRQ from unknow source!");
441 return IRQ_NONE;
442}
443
444/*
445==============================================================================
446 COMMAND MODE
447*/
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400448static void pcl816_cmdtest_out(int e, struct comedi_cmd * cmd)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800449{
450 rt_printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
451 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
452 rt_printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
453 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
454 rt_printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
455 cmd->scan_end_src);
456 rt_printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
457 cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
458}
459
460/*
461==============================================================================
462*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400463static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400464 struct comedi_cmd * cmd)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800465{
466 int err = 0;
467 int tmp, divisor1, divisor2;
468
469 DEBUG(rt_printk("pcl816 pcl812_ai_cmdtest\n");
470 pcl816_cmdtest_out(-1, cmd););
471
472 /* step 1: make sure trigger sources are trivially valid */
473 tmp = cmd->start_src;
474 cmd->start_src &= TRIG_NOW;
475 if (!cmd->start_src || tmp != cmd->start_src)
476 err++;
477
478 tmp = cmd->scan_begin_src;
479 cmd->scan_begin_src &= TRIG_FOLLOW;
480 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
481 err++;
482
483 if (!cmd->convert_src & (TRIG_EXT | TRIG_TIMER))
484 err++;
485
486 tmp = cmd->scan_end_src;
487 cmd->scan_end_src &= TRIG_COUNT;
488 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
489 err++;
490
491 tmp = cmd->stop_src;
492 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
493 if (!cmd->stop_src || tmp != cmd->stop_src)
494 err++;
495
496 if (err) {
497 return 1;
498 }
499
500 /* step 2: make sure trigger sources are unique and mutually compatible */
501
502 if (cmd->start_src != TRIG_NOW) {
503 cmd->start_src = TRIG_NOW;
504 err++;
505 }
506
507 if (cmd->scan_begin_src != TRIG_FOLLOW) {
508 cmd->scan_begin_src = TRIG_FOLLOW;
509 err++;
510 }
511
512 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
513 cmd->convert_src = TRIG_TIMER;
514 err++;
515 }
516
517 if (cmd->scan_end_src != TRIG_COUNT) {
518 cmd->scan_end_src = TRIG_COUNT;
519 err++;
520 }
521
522 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
523 err++;
524
525 if (err) {
526 return 2;
527 }
528
529 /* step 3: make sure arguments are trivially compatible */
530 if (cmd->start_arg != 0) {
531 cmd->start_arg = 0;
532 err++;
533 }
534
535 if (cmd->scan_begin_arg != 0) {
536 cmd->scan_begin_arg = 0;
537 err++;
538 }
539 if (cmd->convert_src == TRIG_TIMER) {
540 if (cmd->convert_arg < this_board->ai_ns_min) {
541 cmd->convert_arg = this_board->ai_ns_min;
542 err++;
543 }
544 } else { /* TRIG_EXT */
545 if (cmd->convert_arg != 0) {
546 cmd->convert_arg = 0;
547 err++;
548 }
549 }
550
551 if (!cmd->chanlist_len) {
552 cmd->chanlist_len = 1;
553 err++;
554 }
555 if (cmd->chanlist_len > this_board->n_aichan) {
556 cmd->chanlist_len = this_board->n_aichan;
557 err++;
558 }
559 if (cmd->scan_end_arg != cmd->chanlist_len) {
560 cmd->scan_end_arg = cmd->chanlist_len;
561 err++;
562 }
563 if (cmd->stop_src == TRIG_COUNT) {
564 if (!cmd->stop_arg) {
565 cmd->stop_arg = 1;
566 err++;
567 }
568 } else { /* TRIG_NONE */
569 if (cmd->stop_arg != 0) {
570 cmd->stop_arg = 0;
571 err++;
572 }
573 }
574
575 if (err) {
576 return 3;
577 }
578
579 /* step 4: fix up any arguments */
580 if (cmd->convert_src == TRIG_TIMER) {
581 tmp = cmd->convert_arg;
582 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
583 &divisor1, &divisor2, &cmd->convert_arg,
584 cmd->flags & TRIG_ROUND_MASK);
585 if (cmd->convert_arg < this_board->ai_ns_min)
586 cmd->convert_arg = this_board->ai_ns_min;
587 if (tmp != cmd->convert_arg)
588 err++;
589 }
590
591 if (err) {
592 return 4;
593 }
594
595 return 0;
596}
597
Bill Pemberton34c43922009-03-16 22:05:14 -0400598static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800599{
600 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400601 struct comedi_cmd *cmd = &s->async->cmd;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800602
603 if (cmd->start_src != TRIG_NOW)
604 return -EINVAL;
605 if (cmd->scan_begin_src != TRIG_FOLLOW)
606 return -EINVAL;
607 if (cmd->scan_end_src != TRIG_COUNT)
608 return -EINVAL;
609 if (cmd->scan_end_arg != cmd->chanlist_len)
610 return -EINVAL;
611// if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL;
612 if (devpriv->irq_blocked)
613 return -EBUSY;
614
615 if (cmd->convert_src == TRIG_TIMER) {
616 if (cmd->convert_arg < this_board->ai_ns_min)
617 cmd->convert_arg = this_board->ai_ns_min;
618
619 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
620 &divisor2, &cmd->convert_arg,
621 cmd->flags & TRIG_ROUND_MASK);
622 if (divisor1 == 1) { // PCL816 crash if any divisor is set to 1
623 divisor1 = 2;
624 divisor2 /= 2;
625 }
626 if (divisor2 == 1) {
627 divisor2 = 2;
628 divisor1 /= 2;
629 }
630 }
631
632 start_pacer(dev, -1, 0, 0); // stop pacer
633
634 if (!check_and_setup_channel_list(dev, s, cmd->chanlist,
635 cmd->chanlist_len))
636 return -EINVAL;
637 comedi_udelay(1);
638
639 devpriv->ai_act_scan = 0;
640 s->async->cur_chan = 0;
641 devpriv->irq_blocked = 1;
642 devpriv->ai_poll_ptr = 0;
643 devpriv->irq_was_now_closed = 0;
644
645 if (cmd->stop_src == TRIG_COUNT) {
646 devpriv->ai_scans = cmd->stop_arg;
647 devpriv->ai_neverending = 0;
648 } else {
649 devpriv->ai_scans = 0;
650 devpriv->ai_neverending = 1;
651 }
652
653 if ((cmd->flags & TRIG_WAKE_EOS)) { // don't we want wake up every scan?
654 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
655 // devpriv->ai_eos=1;
656 //if (devpriv->ai_n_chan==1)
657 // devpriv->dma=0; // DMA is useless for this situation
658 }
659
660 if (devpriv->dma) {
661 bytes = devpriv->hwdmasize[0];
662 if (!devpriv->ai_neverending) {
Bill Pemberton790c5542009-03-16 22:05:02 -0400663 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); // how many
Juan Grigeradd2996b2009-02-19 09:30:57 -0800664 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; // how many DMA pages we must fill
665 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; //on last dma transfer must be moved
666 devpriv->dma_runs_to_end--;
667 if (devpriv->dma_runs_to_end >= 0)
668 bytes = devpriv->hwdmasize[0];
669 } else
670 devpriv->dma_runs_to_end = -1;
671
672 devpriv->next_dma_buf = 0;
673 set_dma_mode(devpriv->dma, DMA_MODE_READ);
674 dma_flags = claim_dma_lock();
675 clear_dma_ff(devpriv->dma);
676 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
677 set_dma_count(devpriv->dma, bytes);
678 release_dma_lock(dma_flags);
679 enable_dma(devpriv->dma);
680 }
681
682 start_pacer(dev, 1, divisor1, divisor2);
683 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
684
685 switch (cmd->convert_src) {
686 case TRIG_TIMER:
687 devpriv->int816_mode = INT_TYPE_AI1_DMA;
688 outb(0x32, dev->iobase + PCL816_CONTROL); // Pacer+IRQ+DMA
689 outb(dmairq, dev->iobase + PCL816_STATUS); // write irq and DMA to card
690 break;
691
692 default:
693 devpriv->int816_mode = INT_TYPE_AI3_DMA;
694 outb(0x34, dev->iobase + PCL816_CONTROL); // Ext trig+IRQ+DMA
695 outb(dmairq, dev->iobase + PCL816_STATUS); // write irq to card
696 break;
697 }
698
699 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
700 return 0;
701}
702
Bill Pemberton34c43922009-03-16 22:05:14 -0400703static int pcl816_ai_poll(struct comedi_device * dev, struct comedi_subdevice * s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800704{
705 unsigned long flags;
706 unsigned int top1, top2, i;
707
708 if (!devpriv->dma)
709 return 0; // poll is valid only for DMA transfer
710
711 comedi_spin_lock_irqsave(&dev->spinlock, flags);
712
713 for (i = 0; i < 20; i++) {
714 top1 = get_dma_residue(devpriv->dma); // where is now DMA
715 top2 = get_dma_residue(devpriv->dma);
716 if (top1 == top2)
717 break;
718 }
719 if (top1 != top2) {
720 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
721 return 0;
722 }
723
724 top1 = devpriv->hwdmasize[0] - top1; // where is now DMA in buffer
725 top1 >>= 1; // sample position
726 top2 = top1 - devpriv->ai_poll_ptr;
727 if (top2 < 1) { // no new samples
728 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
729 return 0;
730 }
731
732 transfer_from_dma_buf(dev, s,
Bill Pemberton790c5542009-03-16 22:05:02 -0400733 (short *) devpriv->dmabuf[devpriv->next_dma_buf],
Juan Grigeradd2996b2009-02-19 09:30:57 -0800734 devpriv->ai_poll_ptr, top2);
735
736 devpriv->ai_poll_ptr = top1; // new buffer position
737 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
738
739 return s->async->buf_write_count - s->async->buf_read_count;
740}
741
742/*
743==============================================================================
744 cancel any mode 1-4 AI
745*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400746static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800747{
748// DEBUG(rt_printk("pcl816_ai_cancel()\n");)
749
750 if (devpriv->irq_blocked > 0) {
751 switch (devpriv->int816_mode) {
752#ifdef unused
753 case INT_TYPE_AI1_DMA_RTC:
754 case INT_TYPE_AI3_DMA_RTC:
755 set_rtc_irq_bit(0); // stop RTC
756 del_timer(&devpriv->rtc_irq_timer);
757#endif
758 case INT_TYPE_AI1_DMA:
759 case INT_TYPE_AI3_DMA:
760 disable_dma(devpriv->dma);
761 case INT_TYPE_AI1_INT:
762 case INT_TYPE_AI3_INT:
763 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */
764 comedi_udelay(1);
765 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
766 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
767 outb(0x70, dev->iobase + PCL816_CTRCTL);
768 outb(0, dev->iobase + PCL816_AD_LO);
769 inb(dev->iobase + PCL816_AD_LO);
770 inb(dev->iobase + PCL816_AD_HI);
771 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
772 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
773 devpriv->irq_blocked = 0;
774 devpriv->irq_was_now_closed = devpriv->int816_mode;
775 devpriv->int816_mode = 0;
776 devpriv->last_int_sub = s;
777// s->busy = 0;
778 break;
779 }
780 }
781
782 DEBUG(rt_printk("comedi: pcl816_ai_cancel() successful\n");
783 )
784 return 0;
785}
786
787/*
788==============================================================================
789 chech for PCL816
790*/
791static int pcl816_check(unsigned long iobase)
792{
793 outb(0x00, iobase + PCL816_MUX);
794 comedi_udelay(1);
795 if (inb(iobase + PCL816_MUX) != 0x00)
796 return 1; //there isn't card
797 outb(0x55, iobase + PCL816_MUX);
798 comedi_udelay(1);
799 if (inb(iobase + PCL816_MUX) != 0x55)
800 return 1; //there isn't card
801 outb(0x00, iobase + PCL816_MUX);
802 comedi_udelay(1);
803 outb(0x18, iobase + PCL816_CONTROL);
804 comedi_udelay(1);
805 if (inb(iobase + PCL816_CONTROL) != 0x18)
806 return 1; //there isn't card
807 return 0; // ok, card exist
808}
809
810/*
811==============================================================================
812 reset whole PCL-816 cards
813*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400814static void pcl816_reset(struct comedi_device * dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800815{
816// outb (0, dev->iobase + PCL818_DA_LO); // DAC=0V
817// outb (0, dev->iobase + PCL818_DA_HI);
818// comedi_udelay (1);
819// outb (0, dev->iobase + PCL818_DO_HI); // DO=$0000
820// outb (0, dev->iobase + PCL818_DO_LO);
821// comedi_udelay (1);
822 outb(0, dev->iobase + PCL816_CONTROL);
823 outb(0, dev->iobase + PCL816_MUX);
824 outb(0, dev->iobase + PCL816_CLRINT);
825 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
826 outb(0x70, dev->iobase + PCL816_CTRCTL);
827 outb(0x30, dev->iobase + PCL816_CTRCTL);
828 outb(0, dev->iobase + PCL816_RANGE);
829}
830
831/*
832==============================================================================
833 Start/stop pacer onboard pacer
834*/
835static void
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400836start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
Juan Grigeradd2996b2009-02-19 09:30:57 -0800837 unsigned int divisor2)
838{
839 outb(0x32, dev->iobase + PCL816_CTRCTL);
840 outb(0xff, dev->iobase + PCL816_CTR0);
841 outb(0x00, dev->iobase + PCL816_CTR0);
842 comedi_udelay(1);
843 outb(0xb4, dev->iobase + PCL816_CTRCTL); // set counter 2 as mode 3
844 outb(0x74, dev->iobase + PCL816_CTRCTL); // set counter 1 as mode 3
845 comedi_udelay(1);
846
847 if (mode == 1) {
848 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
849 divisor2);
850 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
851 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
852 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
853 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
854 }
855
856 /* clear pending interrupts (just in case) */
857// outb(0, dev->iobase + PCL816_CLRINT);
858}
859
860/*
861==============================================================================
862 Check if channel list from user is builded correctly
863 If it's ok, then program scan/gain logic
864*/
865static int
Bill Pemberton34c43922009-03-16 22:05:14 -0400866check_and_setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
Juan Grigeradd2996b2009-02-19 09:30:57 -0800867 unsigned int *chanlist, int chanlen)
868{
869 unsigned int chansegment[16];
870 unsigned int i, nowmustbechan, seglen, segpos;
871
872 // correct channel and range number check itself comedi/range.c
873 if (chanlen < 1) {
874 comedi_error(dev, "range/channel list is empty!");
875 return 0;
876 }
877
878 if (chanlen > 1) {
879 chansegment[0] = chanlist[0]; // first channel is everytime ok
880 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
881 // build part of chanlist
882 DEBUG(rt_printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
883 CR_RANGE(chanlist[i]));
884 )
885 if (chanlist[0] == chanlist[i])
886 break; // we detect loop, this must by finish
887 nowmustbechan =
888 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
889 if (nowmustbechan != CR_CHAN(chanlist[i])) {
890 // channel list isn't continous :-(
891 rt_printk
892 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
893 dev->minor, i, CR_CHAN(chanlist[i]),
894 nowmustbechan, CR_CHAN(chanlist[0]));
895 return 0;
896 }
897 chansegment[i] = chanlist[i]; // well, this is next correct channel in list
898 }
899
900 for (i = 0, segpos = 0; i < chanlen; i++) { // check whole chanlist
901 DEBUG(rt_printk("%d %d=%d %d\n",
902 CR_CHAN(chansegment[i % seglen]),
903 CR_RANGE(chansegment[i % seglen]),
904 CR_CHAN(chanlist[i]),
905 CR_RANGE(chanlist[i]));
906 )
907 if (chanlist[i] != chansegment[i % seglen]) {
908 rt_printk
909 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
910 dev->minor, i, CR_CHAN(chansegment[i]),
911 CR_RANGE(chansegment[i]),
912 CR_AREF(chansegment[i]),
913 CR_CHAN(chanlist[i % seglen]),
914 CR_RANGE(chanlist[i % seglen]),
915 CR_AREF(chansegment[i % seglen]));
916 return 0; // chan/gain list is strange
917 }
918 }
919 } else {
920 seglen = 1;
921 }
922
923 devpriv->ai_act_chanlist_len = seglen;
924 devpriv->ai_act_chanlist_pos = 0;
925
926 for (i = 0; i < seglen; i++) { // store range list to card
927 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
928 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
929 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */
930 }
931
932 comedi_udelay(1);
933
934 outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */
935
936 return 1; // we can serve this with MUX logic
937}
938
939#ifdef unused
940/*
941==============================================================================
942 Enable(1)/disable(0) periodic interrupts from RTC
943*/
944static int set_rtc_irq_bit(unsigned char bit)
945{
946 unsigned char val;
947 unsigned long flags;
948
949 if (bit == 1) {
950 RTC_timer_lock++;
951 if (RTC_timer_lock > 1)
952 return 0;
953 } else {
954 RTC_timer_lock--;
955 if (RTC_timer_lock < 0)
956 RTC_timer_lock = 0;
957 if (RTC_timer_lock > 0)
958 return 0;
959 }
960
961 save_flags(flags);
962 cli();
963 val = CMOS_READ(RTC_CONTROL);
964 if (bit) {
965 val |= RTC_PIE;
966 } else {
967 val &= ~RTC_PIE;
968 }
969 CMOS_WRITE(val, RTC_CONTROL);
970 CMOS_READ(RTC_INTR_FLAGS);
971 restore_flags(flags);
972 return 0;
973}
974#endif
975
976/*
977==============================================================================
978 Free any resources that we have claimed
979*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400980static void free_resources(struct comedi_device * dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800981{
982 //rt_printk("free_resource()\n");
983 if (dev->private) {
984 pcl816_ai_cancel(dev, devpriv->sub_ai);
985 pcl816_reset(dev);
986 if (devpriv->dma)
987 free_dma(devpriv->dma);
988 if (devpriv->dmabuf[0])
989 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
990 if (devpriv->dmabuf[1])
991 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
992#ifdef unused
993 if (devpriv->rtc_irq)
994 comedi_free_irq(devpriv->rtc_irq, dev);
995 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
996 if (devpriv->rtc_iobase)
997 release_region(devpriv->rtc_iobase,
998 devpriv->rtc_iosize);
999 }
1000#endif
1001 }
1002
1003 if (dev->irq)
1004 free_irq(dev->irq, dev);
1005 if (dev->iobase)
1006 release_region(dev->iobase, this_board->io_range);
1007 //rt_printk("free_resource() end\n");
1008}
1009
1010/*
1011==============================================================================
1012
1013 Initialization
1014
1015*/
Bill Pemberton0707bb02009-03-16 22:06:20 -04001016static int pcl816_attach(struct comedi_device * dev, struct comedi_devconfig * it)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001017{
1018 int ret;
1019 unsigned long iobase;
1020 unsigned int irq, dma;
1021 unsigned long pages;
1022 //int i;
Bill Pemberton34c43922009-03-16 22:05:14 -04001023 struct comedi_subdevice *s;
Juan Grigeradd2996b2009-02-19 09:30:57 -08001024
1025 /* claim our I/O space */
1026 iobase = it->options[0];
1027 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
1028 this_board->name, iobase);
1029
1030 if (!request_region(iobase, this_board->io_range, "pcl816")) {
1031 rt_printk("I/O port conflict\n");
1032 return -EIO;
1033 }
1034
1035 dev->iobase = iobase;
1036
1037 if (pcl816_check(iobase)) {
1038 rt_printk(", I cann't detect board. FAIL!\n");
1039 return -EIO;
1040 }
1041
Bill Pembertonfe0ff172009-03-16 22:18:43 -04001042 if ((ret = alloc_private(dev, sizeof(struct pcl816_private))) < 0)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001043 return ret; /* Can't alloc mem */
1044
1045 /* set up some name stuff */
1046 dev->board_name = this_board->name;
1047
1048 /* grab our IRQ */
1049 irq = 0;
1050 if (this_board->IRQbits != 0) { /* board support IRQ */
1051 irq = it->options[1];
1052 if (irq) { /* we want to use IRQ */
1053 if (((1 << irq) & this_board->IRQbits) == 0) {
1054 rt_printk
1055 (", IRQ %u is out of allowed range, DISABLING IT",
1056 irq);
1057 irq = 0; /* Bad IRQ */
1058 } else {
1059 if (comedi_request_irq(irq, interrupt_pcl816, 0,
1060 "pcl816", dev)) {
1061 rt_printk
1062 (", unable to allocate IRQ %u, DISABLING IT",
1063 irq);
1064 irq = 0; /* Can't use IRQ */
1065 } else {
1066 rt_printk(", irq=%u", irq);
1067 }
1068 }
1069 }
1070 }
1071
1072 dev->irq = irq;
1073 if (irq) {
1074 devpriv->irq_free = 1;
1075 } /* 1=we have allocated irq */
1076 else {
1077 devpriv->irq_free = 0;
1078 }
1079 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1080 devpriv->int816_mode = 0; /* mode of irq */
1081
1082#ifdef unused
1083 /* grab RTC for DMA operations */
1084 devpriv->dma_rtc = 0;
1085 if (it->options[2] > 0) { // we want to use DMA
1086 if (RTC_lock == 0) {
1087 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1088 "pcl816 (RTC)"))
1089 goto no_rtc;
1090 }
1091 devpriv->rtc_iobase = RTC_PORT(0);
1092 devpriv->rtc_iosize = RTC_IO_EXTENT;
1093 RTC_lock++;
1094#ifdef UNTESTED_CODE
1095 if (!comedi_request_irq(RTC_IRQ,
1096 interrupt_pcl816_ai_mode13_dma_rtc, 0,
1097 "pcl816 DMA (RTC)", dev)) {
1098 devpriv->dma_rtc = 1;
1099 devpriv->rtc_irq = RTC_IRQ;
1100 rt_printk(", dma_irq=%u", devpriv->rtc_irq);
1101 } else {
1102 RTC_lock--;
1103 if (RTC_lock == 0) {
1104 if (devpriv->rtc_iobase)
1105 release_region(devpriv->rtc_iobase,
1106 devpriv->rtc_iosize);
1107 }
1108 devpriv->rtc_iobase = 0;
1109 devpriv->rtc_iosize = 0;
1110 }
1111#else
1112 printk("pcl816: RTC code missing");
1113#endif
1114
1115 }
1116
1117 no_rtc:
1118#endif
1119 /* grab our DMA */
1120 dma = 0;
1121 devpriv->dma = dma;
1122 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1123 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1124
1125 if (this_board->DMAbits != 0) { /* board support DMA */
1126 dma = it->options[2];
1127 if (dma < 1)
1128 goto no_dma; /* DMA disabled */
1129
1130 if (((1 << dma) & this_board->DMAbits) == 0) {
1131 rt_printk(", DMA is out of allowed range, FAIL!\n");
1132 return -EINVAL; /* Bad DMA */
1133 }
1134 ret = request_dma(dma, "pcl816");
1135 if (ret) {
1136 rt_printk(", unable to allocate DMA %u, FAIL!\n", dma);
1137 return -EBUSY; /* DMA isn't free */
1138 }
1139
1140 devpriv->dma = dma;
1141 rt_printk(", dma=%u", dma);
1142 pages = 2; /* we need 16KB */
1143 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1144
1145 if (!devpriv->dmabuf[0]) {
1146 rt_printk(", unable to allocate DMA buffer, FAIL!\n");
1147 /* maybe experiment with try_to_free_pages() will help .... */
1148 return -EBUSY; /* no buffer :-( */
1149 }
1150 devpriv->dmapages[0] = pages;
1151 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1152 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1153 //rt_printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE);
1154
1155 if (devpriv->dma_rtc == 0) { // we must do duble buff :-(
1156 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1157 if (!devpriv->dmabuf[1]) {
1158 rt_printk
1159 (", unable to allocate DMA buffer, FAIL!\n");
1160 return -EBUSY;
1161 }
1162 devpriv->dmapages[1] = pages;
1163 devpriv->hwdmaptr[1] =
1164 virt_to_bus((void *)devpriv->dmabuf[1]);
1165 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1166 }
1167 }
1168
1169 no_dma:
1170
1171/* if (this_board->n_aochan > 0)
1172 subdevs[1] = COMEDI_SUBD_AO;
1173 if (this_board->n_dichan > 0)
1174 subdevs[2] = COMEDI_SUBD_DI;
1175 if (this_board->n_dochan > 0)
1176 subdevs[3] = COMEDI_SUBD_DO;
1177*/
1178 if ((ret = alloc_subdevices(dev, 1)) < 0)
1179 return ret;
1180
1181 s = dev->subdevices + 0;
1182 if (this_board->n_aichan > 0) {
1183 s->type = COMEDI_SUBD_AI;
1184 devpriv->sub_ai = s;
1185 dev->read_subdev = s;
1186 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1187 s->n_chan = this_board->n_aichan;
1188 s->subdev_flags |= SDF_DIFF;
1189 //printk (", %dchans DIFF DAC - %d", s->n_chan, i);
1190 s->maxdata = this_board->ai_maxdata;
1191 s->len_chanlist = this_board->ai_chanlist;
1192 s->range_table = this_board->ai_range_type;
1193 s->cancel = pcl816_ai_cancel;
1194 s->do_cmdtest = pcl816_ai_cmdtest;
1195 s->do_cmd = pcl816_ai_cmd;
1196 s->poll = pcl816_ai_poll;
1197 s->insn_read = pcl816_ai_insn_read;
1198 } else {
1199 s->type = COMEDI_SUBD_UNUSED;
1200 }
1201
1202#if 0
1203case COMEDI_SUBD_AO:
1204 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1205 s->n_chan = this_board->n_aochan;
1206 s->maxdata = this_board->ao_maxdata;
1207 s->len_chanlist = this_board->ao_chanlist;
1208 s->range_table = this_board->ao_range_type;
1209 break;
1210
1211case COMEDI_SUBD_DI:
1212 s->subdev_flags = SDF_READABLE;
1213 s->n_chan = this_board->n_dichan;
1214 s->maxdata = 1;
1215 s->len_chanlist = this_board->n_dichan;
1216 s->range_table = &range_digital;
1217 break;
1218
1219case COMEDI_SUBD_DO:
1220 s->subdev_flags = SDF_WRITABLE;
1221 s->n_chan = this_board->n_dochan;
1222 s->maxdata = 1;
1223 s->len_chanlist = this_board->n_dochan;
1224 s->range_table = &range_digital;
1225 break;
1226#endif
1227
1228 pcl816_reset(dev);
1229
1230 rt_printk("\n");
1231
1232 return 0;
1233}
1234
1235/*
1236==============================================================================
1237 Removes device
1238 */
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001239static int pcl816_detach(struct comedi_device * dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001240{
1241 DEBUG(rt_printk("comedi%d: pcl816: remove\n", dev->minor);
1242 )
1243 free_resources(dev);
1244#ifdef unused
1245 if (devpriv->dma_rtc)
1246 RTC_lock--;
1247#endif
1248 return 0;
1249}