blob: 2241fa9f5b63d61f88347bdd623d2f50441adfb6 [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
Bill Pemberton58c05762009-03-27 11:31:22 -040046/* boards constants */
47/* IO space len */
Juan Grigeradd2996b2009-02-19 09:30:57 -080048#define PCLx1x_RANGE 16
49
Bill Pemberton58c05762009-03-27 11:31:22 -040050/* #define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
Juan Grigeradd2996b2009-02-19 09:30:57 -080051
Bill Pemberton58c05762009-03-27 11:31:22 -040052/* INTEL 8254 counters */
Juan Grigeradd2996b2009-02-19 09:30:57 -080053#define PCL816_CTR0 4
54#define PCL816_CTR1 5
55#define PCL816_CTR2 6
Bill Pemberton58c05762009-03-27 11:31:22 -040056/* R: counter read-back register W: counter control */
Juan Grigeradd2996b2009-02-19 09:30:57 -080057#define PCL816_CTRCTL 7
58
Bill Pemberton58c05762009-03-27 11:31:22 -040059/* R: A/D high byte W: A/D range control */
Juan Grigeradd2996b2009-02-19 09:30:57 -080060#define PCL816_RANGE 9
Bill Pemberton58c05762009-03-27 11:31:22 -040061/* W: clear INT request */
Juan Grigeradd2996b2009-02-19 09:30:57 -080062#define PCL816_CLRINT 10
Bill Pemberton58c05762009-03-27 11:31:22 -040063/* R: next mux scan channel W: mux scan channel & range control pointer */
Juan Grigeradd2996b2009-02-19 09:30:57 -080064#define PCL816_MUX 11
Bill Pemberton58c05762009-03-27 11:31:22 -040065/* R/W: operation control register */
Juan Grigeradd2996b2009-02-19 09:30:57 -080066#define PCL816_CONTROL 12
67
Bill Pemberton58c05762009-03-27 11:31:22 -040068/* R: return status byte W: set DMA/IRQ */
Juan Grigeradd2996b2009-02-19 09:30:57 -080069#define PCL816_STATUS 13
70#define PCL816_STATUS_DRDY_MASK 0x80
71
Bill Pemberton58c05762009-03-27 11:31:22 -040072/* R: low byte of A/D W: soft A/D trigger */
Juan Grigeradd2996b2009-02-19 09:30:57 -080073#define PCL816_AD_LO 8
Bill Pemberton58c05762009-03-27 11:31:22 -040074/* R: high byte of A/D W: A/D range control */
Juan Grigeradd2996b2009-02-19 09:30:57 -080075#define PCL816_AD_HI 9
76
Bill Pemberton58c05762009-03-27 11:31:22 -040077/* type of interrupt handler */
Juan Grigeradd2996b2009-02-19 09:30:57 -080078#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
Bill Pemberton58c05762009-03-27 11:31:22 -040086/* RTC stuff... */
Juan Grigeradd2996b2009-02-19 09:30:57 -080087#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};
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400104struct pcl816_board {
105
Bill Pemberton58c05762009-03-27 11:31:22 -0400106 const char *name; /* board name */
107 int n_ranges; /* len of range list */
108 int n_aichan; /* num of A/D chans in diferencial mode */
109 unsigned int ai_ns_min; /* minimal alllowed delay between samples (in ns) */
110 int n_aochan; /* num of D/A chans */
111 int n_dichan; /* num of DI chans */
112 int n_dochan; /* num of DO chans */
113 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
114 const struct comedi_lrange *ao_range_type; /* dafault D/A rangelist */
115 unsigned int io_range; /* len of IO space */
116 unsigned int IRQbits; /* allowed interrupts */
117 unsigned int DMAbits; /* allowed DMA chans */
118 int ai_maxdata; /* maxdata for A/D */
119 int ao_maxdata; /* maxdata for D/A */
120 int ai_chanlist; /* allowed len of channel list A/D */
121 int ao_chanlist; /* allowed len of channel list D/A */
122 int i8254_osc_base; /* 1/frequency of on board oscilator in ns */
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400123};
Juan Grigeradd2996b2009-02-19 09:30:57 -0800124
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400125
126static const struct pcl816_board boardtypes[] = {
Juan Grigeradd2996b2009-02-19 09:30:57 -0800127 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
128 &range_pcl816, PCLx1x_RANGE,
Bill Pemberton58c05762009-03-27 11:31:22 -0400129 0x00fc, /* IRQ mask */
130 0x0a, /* DMA mask */
131 0xffff, /* 16-bit card */
132 0xffff, /* D/A maxdata */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800133 1024,
Bill Pemberton58c05762009-03-27 11:31:22 -0400134 1, /* ao chan list */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800135 100},
136 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
137 &range_pcl816, PCLx1x_RANGE,
138 0x00fc,
139 0x0a,
140 0x3fff, /* 14 bit card */
141 0x3fff,
142 1024,
143 1,
144 100},
145};
146
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400147#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board))
Bill Pembertonfe0ff172009-03-16 22:18:43 -0400148#define devpriv ((struct pcl816_private *)dev->private)
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400149#define this_board ((const struct pcl816_board *)dev->board_ptr)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800150
Bill Pemberton0707bb02009-03-16 22:06:20 -0400151static int pcl816_attach(struct comedi_device * dev, struct comedi_devconfig * it);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400152static int pcl816_detach(struct comedi_device * dev);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800153
154#ifdef unused
155static int RTC_lock = 0; /* RTC lock */
156static int RTC_timer_lock = 0; /* RTC int lock */
157#endif
158
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400159static struct comedi_driver driver_pcl816 = {
Juan Grigeradd2996b2009-02-19 09:30:57 -0800160 driver_name:"pcl816",
161 module:THIS_MODULE,
162 attach:pcl816_attach,
163 detach:pcl816_detach,
164 board_name:&boardtypes[0].name,
165 num_names:n_boardtypes,
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400166 offset:sizeof(struct pcl816_board),
Juan Grigeradd2996b2009-02-19 09:30:57 -0800167};
168
169COMEDI_INITCLEANUP(driver_pcl816);
170
Bill Pembertonfe0ff172009-03-16 22:18:43 -0400171struct pcl816_private {
172
Bill Pemberton58c05762009-03-27 11:31:22 -0400173 unsigned int dma; /* used DMA, 0=don't use DMA */
174 int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800175#ifdef unused
Bill Pemberton58c05762009-03-27 11:31:22 -0400176 unsigned long rtc_iobase; /* RTC port region */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800177 unsigned int rtc_iosize;
178 unsigned int rtc_irq;
179#endif
Bill Pemberton58c05762009-03-27 11:31:22 -0400180 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
181 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
182 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
183 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
184 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
185 unsigned int last_top_dma; /* DMA pointer in last RTC int */
186 int next_dma_buf; /* which DMA buffer will be used next round */
187 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
188 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800189
Bill Pemberton58c05762009-03-27 11:31:22 -0400190 unsigned int ai_scans; /* len of scanlist */
191 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
192 int irq_free; /* 1=have allocated IRQ */
193 int irq_blocked; /* 1=IRQ now uses any subdev */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800194#ifdef unused
Bill Pemberton58c05762009-03-27 11:31:22 -0400195 int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800196#endif
Bill Pemberton58c05762009-03-27 11:31:22 -0400197 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
198 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
199 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
200 int ai_act_scan; /* how many scans we finished */
201 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
202 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
203 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
204 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
205 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800206#ifdef unused
Bill Pemberton58c05762009-03-27 11:31:22 -0400207 struct timer_list rtc_irq_timer; /* timer for RTC sanity check */
208 unsigned long rtc_freq; /* RTC int freq */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800209#endif
Bill Pembertonfe0ff172009-03-16 22:18:43 -0400210};
211
Juan Grigeradd2996b2009-02-19 09:30:57 -0800212
213/*
214==============================================================================
215*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400216static int check_and_setup_channel_list(struct comedi_device * dev,
Bill Pemberton34c43922009-03-16 22:05:14 -0400217 struct comedi_subdevice * s, unsigned int *chanlist, int chanlen);
218static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400219static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
Juan Grigeradd2996b2009-02-19 09:30:57 -0800220 unsigned int divisor2);
221#ifdef unused
222static int set_rtc_irq_bit(unsigned char bit);
223#endif
224
Bill Pemberton34c43922009-03-16 22:05:14 -0400225static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400226 struct comedi_cmd * cmd);
Bill Pemberton34c43922009-03-16 22:05:14 -0400227static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800228
229/*
230==============================================================================
231 ANALOG INPUT MODE0, 816 cards, slow version
232*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400233static int pcl816_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
Bill Pemberton90035c02009-03-16 22:05:53 -0400234 struct comedi_insn * insn, unsigned int * data)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800235{
236 int n;
237 int timeout;
238
239 DPRINTK("mode 0 analog input\n");
Bill Pemberton58c05762009-03-27 11:31:22 -0400240 /* software trigger, DMA and INT off */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800241 outb(0, dev->iobase + PCL816_CONTROL);
Bill Pemberton58c05762009-03-27 11:31:22 -0400242 /* clear INT (conversion end) flag */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800243 outb(0, dev->iobase + PCL816_CLRINT);
244
Bill Pemberton58c05762009-03-27 11:31:22 -0400245 /* Set the input channel */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800246 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
247 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */
248
249 for (n = 0; n < insn->n; n++) {
250
251 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
252
253 timeout = 100;
254 while (timeout--) {
255 if (!(inb(dev->iobase + PCL816_STATUS) &
256 PCL816_STATUS_DRDY_MASK)) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400257 /* return read value */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800258 data[n] =
259 ((inb(dev->iobase +
260 PCL816_AD_HI) << 8) |
261 (inb(dev->iobase + PCL816_AD_LO)));
262
263 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
264 break;
265 }
266 comedi_udelay(1);
267 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400268 /* Return timeout error */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800269 if (!timeout) {
270 comedi_error(dev, "A/D insn timeout\n");
271 data[0] = 0;
272 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
273 return -EIO;
274 }
275
276 }
277 return n;
278}
279
280/*
281==============================================================================
282 analog input interrupt mode 1 & 3, 818 cards
283 one sample per interrupt version
284*/
285static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
286{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400287 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400288 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800289 int low, hi;
290 int timeout = 50; /* wait max 50us */
291
292 while (timeout--) {
293 if (!(inb(dev->iobase + PCL816_STATUS) &
294 PCL816_STATUS_DRDY_MASK))
295 break;
296 comedi_udelay(1);
297 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400298 if (!timeout) { /* timeout, bail error */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800299 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
300 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
301 pcl816_ai_cancel(dev, s);
302 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
303 comedi_event(dev, s);
304 return IRQ_HANDLED;
305
306 }
307
Bill Pemberton58c05762009-03-27 11:31:22 -0400308 /* get the sample */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800309 low = inb(dev->iobase + PCL816_AD_LO);
310 hi = inb(dev->iobase + PCL816_AD_HI);
311
312 comedi_buf_put(s->async, (hi << 8) | low);
313
314 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
315
316 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
317 devpriv->ai_act_chanlist_pos = 0;
318
319 if (s->async->cur_chan == 0) {
320 devpriv->ai_act_scan++;
321 }
322
323 if (!devpriv->ai_neverending)
324 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
325 /* all data sampled */
326 pcl816_ai_cancel(dev, s);
327 s->async->events |= COMEDI_CB_EOA;
328 }
329 comedi_event(dev, s);
330 return IRQ_HANDLED;
331}
332
333/*
334==============================================================================
335 analog input dma mode 1 & 3, 816 cards
336*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400337static void transfer_from_dma_buf(struct comedi_device * dev, struct comedi_subdevice * s,
Bill Pemberton790c5542009-03-16 22:05:02 -0400338 short * ptr, unsigned int bufptr, unsigned int len)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800339{
340 int i;
341
342 s->async->events = 0;
343
344 for (i = 0; i < len; i++) {
345
346 comedi_buf_put(s->async, ptr[bufptr++]);
347
348 if (++devpriv->ai_act_chanlist_pos >=
349 devpriv->ai_act_chanlist_len) {
350 devpriv->ai_act_chanlist_pos = 0;
351 devpriv->ai_act_scan++;
352 }
353
354 if (!devpriv->ai_neverending)
Bill Pemberton58c05762009-03-27 11:31:22 -0400355 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800356 pcl816_ai_cancel(dev, s);
357 s->async->events |= COMEDI_CB_EOA;
358 s->async->events |= COMEDI_CB_BLOCK;
359 break;
360 }
361 }
362
363 comedi_event(dev, s);
364}
365
366static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
367{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400368 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400369 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800370 int len, bufptr, this_dma_buf;
371 unsigned long dma_flags;
Bill Pemberton790c5542009-03-16 22:05:02 -0400372 short *ptr;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800373
374 disable_dma(devpriv->dma);
375 this_dma_buf = devpriv->next_dma_buf;
376
Bill Pemberton58c05762009-03-27 11:31:22 -0400377 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { /* switch dma bufs */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800378
379 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
380 set_dma_mode(devpriv->dma, DMA_MODE_READ);
381 dma_flags = claim_dma_lock();
Bill Pemberton58c05762009-03-27 11:31:22 -0400382/* clear_dma_ff (devpriv->dma); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800383 set_dma_addr(devpriv->dma,
384 devpriv->hwdmaptr[devpriv->next_dma_buf]);
385 if (devpriv->dma_runs_to_end) {
386 set_dma_count(devpriv->dma,
387 devpriv->hwdmasize[devpriv->next_dma_buf]);
388 } else {
389 set_dma_count(devpriv->dma, devpriv->last_dma_run);
390 }
391 release_dma_lock(dma_flags);
392 enable_dma(devpriv->dma);
393 }
394
395 devpriv->dma_runs_to_end--;
396 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
397
Bill Pemberton790c5542009-03-16 22:05:02 -0400398 ptr = (short *) devpriv->dmabuf[this_dma_buf];
Juan Grigeradd2996b2009-02-19 09:30:57 -0800399
400 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
401 bufptr = devpriv->ai_poll_ptr;
402 devpriv->ai_poll_ptr = 0;
403
404 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
405 return IRQ_HANDLED;
406}
407
408/*
409==============================================================================
410 INT procedure
411*/
Jiri Slaby70265d22009-03-26 09:34:06 +0100412static irqreturn_t interrupt_pcl816(int irq, void *d)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800413{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400414 struct comedi_device *dev = d;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800415 DPRINTK("<I>");
416
417 if (!dev->attached) {
418 comedi_error(dev, "premature interrupt");
419 return IRQ_HANDLED;
420 }
421
422 switch (devpriv->int816_mode) {
423 case INT_TYPE_AI1_DMA:
424 case INT_TYPE_AI3_DMA:
425 return interrupt_pcl816_ai_mode13_dma(irq, d);
426 case INT_TYPE_AI1_INT:
427 case INT_TYPE_AI3_INT:
428 return interrupt_pcl816_ai_mode13_int(irq, d);
429 }
430
431 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
432 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
433 (!devpriv->int816_mode)) {
434 if (devpriv->irq_was_now_closed) {
435 devpriv->irq_was_now_closed = 0;
Bill Pemberton58c05762009-03-27 11:31:22 -0400436 /* comedi_error(dev,"last IRQ.."); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800437 return IRQ_HANDLED;
438 }
439 comedi_error(dev, "bad IRQ!");
440 return IRQ_NONE;
441 }
442 comedi_error(dev, "IRQ from unknow source!");
443 return IRQ_NONE;
444}
445
446/*
447==============================================================================
448 COMMAND MODE
449*/
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400450static void pcl816_cmdtest_out(int e, struct comedi_cmd * cmd)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800451{
452 rt_printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
453 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
454 rt_printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
455 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
456 rt_printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
457 cmd->scan_end_src);
458 rt_printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
459 cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
460}
461
462/*
463==============================================================================
464*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400465static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400466 struct comedi_cmd * cmd)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800467{
468 int err = 0;
469 int tmp, divisor1, divisor2;
470
471 DEBUG(rt_printk("pcl816 pcl812_ai_cmdtest\n");
472 pcl816_cmdtest_out(-1, cmd););
473
474 /* step 1: make sure trigger sources are trivially valid */
475 tmp = cmd->start_src;
476 cmd->start_src &= TRIG_NOW;
477 if (!cmd->start_src || tmp != cmd->start_src)
478 err++;
479
480 tmp = cmd->scan_begin_src;
481 cmd->scan_begin_src &= TRIG_FOLLOW;
482 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
483 err++;
484
485 if (!cmd->convert_src & (TRIG_EXT | TRIG_TIMER))
486 err++;
487
488 tmp = cmd->scan_end_src;
489 cmd->scan_end_src &= TRIG_COUNT;
490 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
491 err++;
492
493 tmp = cmd->stop_src;
494 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
495 if (!cmd->stop_src || tmp != cmd->stop_src)
496 err++;
497
498 if (err) {
499 return 1;
500 }
501
502 /* step 2: make sure trigger sources are unique and mutually compatible */
503
504 if (cmd->start_src != TRIG_NOW) {
505 cmd->start_src = TRIG_NOW;
506 err++;
507 }
508
509 if (cmd->scan_begin_src != TRIG_FOLLOW) {
510 cmd->scan_begin_src = TRIG_FOLLOW;
511 err++;
512 }
513
514 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
515 cmd->convert_src = TRIG_TIMER;
516 err++;
517 }
518
519 if (cmd->scan_end_src != TRIG_COUNT) {
520 cmd->scan_end_src = TRIG_COUNT;
521 err++;
522 }
523
524 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
525 err++;
526
527 if (err) {
528 return 2;
529 }
530
531 /* step 3: make sure arguments are trivially compatible */
532 if (cmd->start_arg != 0) {
533 cmd->start_arg = 0;
534 err++;
535 }
536
537 if (cmd->scan_begin_arg != 0) {
538 cmd->scan_begin_arg = 0;
539 err++;
540 }
541 if (cmd->convert_src == TRIG_TIMER) {
542 if (cmd->convert_arg < this_board->ai_ns_min) {
543 cmd->convert_arg = this_board->ai_ns_min;
544 err++;
545 }
546 } else { /* TRIG_EXT */
547 if (cmd->convert_arg != 0) {
548 cmd->convert_arg = 0;
549 err++;
550 }
551 }
552
553 if (!cmd->chanlist_len) {
554 cmd->chanlist_len = 1;
555 err++;
556 }
557 if (cmd->chanlist_len > this_board->n_aichan) {
558 cmd->chanlist_len = this_board->n_aichan;
559 err++;
560 }
561 if (cmd->scan_end_arg != cmd->chanlist_len) {
562 cmd->scan_end_arg = cmd->chanlist_len;
563 err++;
564 }
565 if (cmd->stop_src == TRIG_COUNT) {
566 if (!cmd->stop_arg) {
567 cmd->stop_arg = 1;
568 err++;
569 }
570 } else { /* TRIG_NONE */
571 if (cmd->stop_arg != 0) {
572 cmd->stop_arg = 0;
573 err++;
574 }
575 }
576
577 if (err) {
578 return 3;
579 }
580
581 /* step 4: fix up any arguments */
582 if (cmd->convert_src == TRIG_TIMER) {
583 tmp = cmd->convert_arg;
584 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
585 &divisor1, &divisor2, &cmd->convert_arg,
586 cmd->flags & TRIG_ROUND_MASK);
587 if (cmd->convert_arg < this_board->ai_ns_min)
588 cmd->convert_arg = this_board->ai_ns_min;
589 if (tmp != cmd->convert_arg)
590 err++;
591 }
592
593 if (err) {
594 return 4;
595 }
596
597 return 0;
598}
599
Bill Pemberton34c43922009-03-16 22:05:14 -0400600static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800601{
602 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400603 struct comedi_cmd *cmd = &s->async->cmd;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800604
605 if (cmd->start_src != TRIG_NOW)
606 return -EINVAL;
607 if (cmd->scan_begin_src != TRIG_FOLLOW)
608 return -EINVAL;
609 if (cmd->scan_end_src != TRIG_COUNT)
610 return -EINVAL;
611 if (cmd->scan_end_arg != cmd->chanlist_len)
612 return -EINVAL;
Bill Pemberton58c05762009-03-27 11:31:22 -0400613/* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800614 if (devpriv->irq_blocked)
615 return -EBUSY;
616
617 if (cmd->convert_src == TRIG_TIMER) {
618 if (cmd->convert_arg < this_board->ai_ns_min)
619 cmd->convert_arg = this_board->ai_ns_min;
620
621 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
622 &divisor2, &cmd->convert_arg,
623 cmd->flags & TRIG_ROUND_MASK);
Bill Pemberton58c05762009-03-27 11:31:22 -0400624 if (divisor1 == 1) { /* PCL816 crash if any divisor is set to 1 */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800625 divisor1 = 2;
626 divisor2 /= 2;
627 }
628 if (divisor2 == 1) {
629 divisor2 = 2;
630 divisor1 /= 2;
631 }
632 }
633
Bill Pemberton58c05762009-03-27 11:31:22 -0400634 start_pacer(dev, -1, 0, 0); /* stop pacer */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800635
636 if (!check_and_setup_channel_list(dev, s, cmd->chanlist,
637 cmd->chanlist_len))
638 return -EINVAL;
639 comedi_udelay(1);
640
641 devpriv->ai_act_scan = 0;
642 s->async->cur_chan = 0;
643 devpriv->irq_blocked = 1;
644 devpriv->ai_poll_ptr = 0;
645 devpriv->irq_was_now_closed = 0;
646
647 if (cmd->stop_src == TRIG_COUNT) {
648 devpriv->ai_scans = cmd->stop_arg;
649 devpriv->ai_neverending = 0;
650 } else {
651 devpriv->ai_scans = 0;
652 devpriv->ai_neverending = 1;
653 }
654
Bill Pemberton58c05762009-03-27 11:31:22 -0400655 if ((cmd->flags & TRIG_WAKE_EOS)) { /* don't we want wake up every scan? */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800656 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
Bill Pemberton58c05762009-03-27 11:31:22 -0400657 /* devpriv->ai_eos=1; */
658 /* if (devpriv->ai_n_chan==1) */
659 /* devpriv->dma=0; // DMA is useless for this situation */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800660 }
661
662 if (devpriv->dma) {
663 bytes = devpriv->hwdmasize[0];
664 if (!devpriv->ai_neverending) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400665 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); /* how many */
666 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fill */
667 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800668 devpriv->dma_runs_to_end--;
669 if (devpriv->dma_runs_to_end >= 0)
670 bytes = devpriv->hwdmasize[0];
671 } else
672 devpriv->dma_runs_to_end = -1;
673
674 devpriv->next_dma_buf = 0;
675 set_dma_mode(devpriv->dma, DMA_MODE_READ);
676 dma_flags = claim_dma_lock();
677 clear_dma_ff(devpriv->dma);
678 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
679 set_dma_count(devpriv->dma, bytes);
680 release_dma_lock(dma_flags);
681 enable_dma(devpriv->dma);
682 }
683
684 start_pacer(dev, 1, divisor1, divisor2);
685 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
686
687 switch (cmd->convert_src) {
688 case TRIG_TIMER:
689 devpriv->int816_mode = INT_TYPE_AI1_DMA;
Bill Pemberton58c05762009-03-27 11:31:22 -0400690 outb(0x32, dev->iobase + PCL816_CONTROL); /* Pacer+IRQ+DMA */
691 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq and DMA to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800692 break;
693
694 default:
695 devpriv->int816_mode = INT_TYPE_AI3_DMA;
Bill Pemberton58c05762009-03-27 11:31:22 -0400696 outb(0x34, dev->iobase + PCL816_CONTROL); /* Ext trig+IRQ+DMA */
697 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800698 break;
699 }
700
701 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
702 return 0;
703}
704
Bill Pemberton34c43922009-03-16 22:05:14 -0400705static int pcl816_ai_poll(struct comedi_device * dev, struct comedi_subdevice * s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800706{
707 unsigned long flags;
708 unsigned int top1, top2, i;
709
710 if (!devpriv->dma)
Bill Pemberton58c05762009-03-27 11:31:22 -0400711 return 0; /* poll is valid only for DMA transfer */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800712
713 comedi_spin_lock_irqsave(&dev->spinlock, flags);
714
715 for (i = 0; i < 20; i++) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400716 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800717 top2 = get_dma_residue(devpriv->dma);
718 if (top1 == top2)
719 break;
720 }
721 if (top1 != top2) {
722 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
723 return 0;
724 }
725
Bill Pemberton58c05762009-03-27 11:31:22 -0400726 top1 = devpriv->hwdmasize[0] - top1; /* where is now DMA in buffer */
727 top1 >>= 1; /* sample position */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800728 top2 = top1 - devpriv->ai_poll_ptr;
Bill Pemberton58c05762009-03-27 11:31:22 -0400729 if (top2 < 1) { /* no new samples */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800730 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
731 return 0;
732 }
733
734 transfer_from_dma_buf(dev, s,
Bill Pemberton790c5542009-03-16 22:05:02 -0400735 (short *) devpriv->dmabuf[devpriv->next_dma_buf],
Juan Grigeradd2996b2009-02-19 09:30:57 -0800736 devpriv->ai_poll_ptr, top2);
737
Bill Pemberton58c05762009-03-27 11:31:22 -0400738 devpriv->ai_poll_ptr = top1; /* new buffer position */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800739 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
740
741 return s->async->buf_write_count - s->async->buf_read_count;
742}
743
744/*
745==============================================================================
746 cancel any mode 1-4 AI
747*/
Bill Pemberton34c43922009-03-16 22:05:14 -0400748static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800749{
Bill Pemberton58c05762009-03-27 11:31:22 -0400750/* DEBUG(rt_printk("pcl816_ai_cancel()\n");) */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800751
752 if (devpriv->irq_blocked > 0) {
753 switch (devpriv->int816_mode) {
754#ifdef unused
755 case INT_TYPE_AI1_DMA_RTC:
756 case INT_TYPE_AI3_DMA_RTC:
Bill Pemberton58c05762009-03-27 11:31:22 -0400757 set_rtc_irq_bit(0); /* stop RTC */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800758 del_timer(&devpriv->rtc_irq_timer);
759#endif
760 case INT_TYPE_AI1_DMA:
761 case INT_TYPE_AI3_DMA:
762 disable_dma(devpriv->dma);
763 case INT_TYPE_AI1_INT:
764 case INT_TYPE_AI3_INT:
765 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */
766 comedi_udelay(1);
767 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
768 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
769 outb(0x70, dev->iobase + PCL816_CTRCTL);
770 outb(0, dev->iobase + PCL816_AD_LO);
771 inb(dev->iobase + PCL816_AD_LO);
772 inb(dev->iobase + PCL816_AD_HI);
773 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
774 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
775 devpriv->irq_blocked = 0;
776 devpriv->irq_was_now_closed = devpriv->int816_mode;
777 devpriv->int816_mode = 0;
778 devpriv->last_int_sub = s;
Bill Pemberton58c05762009-03-27 11:31:22 -0400779/* s->busy = 0; */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800780 break;
781 }
782 }
783
784 DEBUG(rt_printk("comedi: pcl816_ai_cancel() successful\n");
785 )
786 return 0;
787}
788
789/*
790==============================================================================
791 chech for PCL816
792*/
793static int pcl816_check(unsigned long iobase)
794{
795 outb(0x00, iobase + PCL816_MUX);
796 comedi_udelay(1);
797 if (inb(iobase + PCL816_MUX) != 0x00)
Bill Pemberton58c05762009-03-27 11:31:22 -0400798 return 1; /* there isn't card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800799 outb(0x55, iobase + PCL816_MUX);
800 comedi_udelay(1);
801 if (inb(iobase + PCL816_MUX) != 0x55)
Bill Pemberton58c05762009-03-27 11:31:22 -0400802 return 1; /* there isn't card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800803 outb(0x00, iobase + PCL816_MUX);
804 comedi_udelay(1);
805 outb(0x18, iobase + PCL816_CONTROL);
806 comedi_udelay(1);
807 if (inb(iobase + PCL816_CONTROL) != 0x18)
Bill Pemberton58c05762009-03-27 11:31:22 -0400808 return 1; /* there isn't card */
809 return 0; /* ok, card exist */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800810}
811
812/*
813==============================================================================
814 reset whole PCL-816 cards
815*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400816static void pcl816_reset(struct comedi_device * dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800817{
Bill Pemberton58c05762009-03-27 11:31:22 -0400818/* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
819/* outb (0, dev->iobase + PCL818_DA_HI); */
820/* comedi_udelay (1); */
821/* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
822/* outb (0, dev->iobase + PCL818_DO_LO); */
823/* comedi_udelay (1); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800824 outb(0, dev->iobase + PCL816_CONTROL);
825 outb(0, dev->iobase + PCL816_MUX);
826 outb(0, dev->iobase + PCL816_CLRINT);
827 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
828 outb(0x70, dev->iobase + PCL816_CTRCTL);
829 outb(0x30, dev->iobase + PCL816_CTRCTL);
830 outb(0, dev->iobase + PCL816_RANGE);
831}
832
833/*
834==============================================================================
835 Start/stop pacer onboard pacer
836*/
837static void
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400838start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
Juan Grigeradd2996b2009-02-19 09:30:57 -0800839 unsigned int divisor2)
840{
841 outb(0x32, dev->iobase + PCL816_CTRCTL);
842 outb(0xff, dev->iobase + PCL816_CTR0);
843 outb(0x00, dev->iobase + PCL816_CTR0);
844 comedi_udelay(1);
Bill Pemberton58c05762009-03-27 11:31:22 -0400845 outb(0xb4, dev->iobase + PCL816_CTRCTL); /* set counter 2 as mode 3 */
846 outb(0x74, dev->iobase + PCL816_CTRCTL); /* set counter 1 as mode 3 */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800847 comedi_udelay(1);
848
849 if (mode == 1) {
850 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
851 divisor2);
852 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
853 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
854 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
855 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
856 }
857
858 /* clear pending interrupts (just in case) */
Bill Pemberton58c05762009-03-27 11:31:22 -0400859/* outb(0, dev->iobase + PCL816_CLRINT); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800860}
861
862/*
863==============================================================================
864 Check if channel list from user is builded correctly
865 If it's ok, then program scan/gain logic
866*/
867static int
Bill Pemberton34c43922009-03-16 22:05:14 -0400868check_and_setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
Juan Grigeradd2996b2009-02-19 09:30:57 -0800869 unsigned int *chanlist, int chanlen)
870{
871 unsigned int chansegment[16];
872 unsigned int i, nowmustbechan, seglen, segpos;
873
Bill Pemberton58c05762009-03-27 11:31:22 -0400874 /* correct channel and range number check itself comedi/range.c */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800875 if (chanlen < 1) {
876 comedi_error(dev, "range/channel list is empty!");
877 return 0;
878 }
879
880 if (chanlen > 1) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400881 chansegment[0] = chanlist[0]; /* first channel is everytime ok */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800882 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400883 /* build part of chanlist */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800884 DEBUG(rt_printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
885 CR_RANGE(chanlist[i]));
886 )
887 if (chanlist[0] == chanlist[i])
Bill Pemberton58c05762009-03-27 11:31:22 -0400888 break; /* we detect loop, this must by finish */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800889 nowmustbechan =
890 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
891 if (nowmustbechan != CR_CHAN(chanlist[i])) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400892 /* channel list isn't continous :-( */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800893 rt_printk
894 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
895 dev->minor, i, CR_CHAN(chanlist[i]),
896 nowmustbechan, CR_CHAN(chanlist[0]));
897 return 0;
898 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400899 chansegment[i] = chanlist[i]; /* well, this is next correct channel in list */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800900 }
901
Bill Pemberton58c05762009-03-27 11:31:22 -0400902 for (i = 0, segpos = 0; i < chanlen; i++) { /* check whole chanlist */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800903 DEBUG(rt_printk("%d %d=%d %d\n",
904 CR_CHAN(chansegment[i % seglen]),
905 CR_RANGE(chansegment[i % seglen]),
906 CR_CHAN(chanlist[i]),
907 CR_RANGE(chanlist[i]));
908 )
909 if (chanlist[i] != chansegment[i % seglen]) {
910 rt_printk
911 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
912 dev->minor, i, CR_CHAN(chansegment[i]),
913 CR_RANGE(chansegment[i]),
914 CR_AREF(chansegment[i]),
915 CR_CHAN(chanlist[i % seglen]),
916 CR_RANGE(chanlist[i % seglen]),
917 CR_AREF(chansegment[i % seglen]));
Bill Pemberton58c05762009-03-27 11:31:22 -0400918 return 0; /* chan/gain list is strange */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800919 }
920 }
921 } else {
922 seglen = 1;
923 }
924
925 devpriv->ai_act_chanlist_len = seglen;
926 devpriv->ai_act_chanlist_pos = 0;
927
Bill Pemberton58c05762009-03-27 11:31:22 -0400928 for (i = 0; i < seglen; i++) { /* store range list to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800929 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
930 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
931 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */
932 }
933
934 comedi_udelay(1);
935
936 outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */
937
Bill Pemberton58c05762009-03-27 11:31:22 -0400938 return 1; /* we can serve this with MUX logic */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800939}
940
941#ifdef unused
942/*
943==============================================================================
944 Enable(1)/disable(0) periodic interrupts from RTC
945*/
946static int set_rtc_irq_bit(unsigned char bit)
947{
948 unsigned char val;
949 unsigned long flags;
950
951 if (bit == 1) {
952 RTC_timer_lock++;
953 if (RTC_timer_lock > 1)
954 return 0;
955 } else {
956 RTC_timer_lock--;
957 if (RTC_timer_lock < 0)
958 RTC_timer_lock = 0;
959 if (RTC_timer_lock > 0)
960 return 0;
961 }
962
963 save_flags(flags);
964 cli();
965 val = CMOS_READ(RTC_CONTROL);
966 if (bit) {
967 val |= RTC_PIE;
968 } else {
969 val &= ~RTC_PIE;
970 }
971 CMOS_WRITE(val, RTC_CONTROL);
972 CMOS_READ(RTC_INTR_FLAGS);
973 restore_flags(flags);
974 return 0;
975}
976#endif
977
978/*
979==============================================================================
980 Free any resources that we have claimed
981*/
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400982static void free_resources(struct comedi_device * dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800983{
Bill Pemberton58c05762009-03-27 11:31:22 -0400984 /* rt_printk("free_resource()\n"); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800985 if (dev->private) {
986 pcl816_ai_cancel(dev, devpriv->sub_ai);
987 pcl816_reset(dev);
988 if (devpriv->dma)
989 free_dma(devpriv->dma);
990 if (devpriv->dmabuf[0])
991 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
992 if (devpriv->dmabuf[1])
993 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
994#ifdef unused
995 if (devpriv->rtc_irq)
996 comedi_free_irq(devpriv->rtc_irq, dev);
997 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
998 if (devpriv->rtc_iobase)
999 release_region(devpriv->rtc_iobase,
1000 devpriv->rtc_iosize);
1001 }
1002#endif
1003 }
1004
1005 if (dev->irq)
1006 free_irq(dev->irq, dev);
1007 if (dev->iobase)
1008 release_region(dev->iobase, this_board->io_range);
Bill Pemberton58c05762009-03-27 11:31:22 -04001009 /* rt_printk("free_resource() end\n"); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001010}
1011
1012/*
1013==============================================================================
1014
1015 Initialization
1016
1017*/
Bill Pemberton0707bb02009-03-16 22:06:20 -04001018static int pcl816_attach(struct comedi_device * dev, struct comedi_devconfig * it)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001019{
1020 int ret;
1021 unsigned long iobase;
1022 unsigned int irq, dma;
1023 unsigned long pages;
Bill Pemberton58c05762009-03-27 11:31:22 -04001024 /* int i; */
Bill Pemberton34c43922009-03-16 22:05:14 -04001025 struct comedi_subdevice *s;
Juan Grigeradd2996b2009-02-19 09:30:57 -08001026
1027 /* claim our I/O space */
1028 iobase = it->options[0];
1029 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
1030 this_board->name, iobase);
1031
1032 if (!request_region(iobase, this_board->io_range, "pcl816")) {
1033 rt_printk("I/O port conflict\n");
1034 return -EIO;
1035 }
1036
1037 dev->iobase = iobase;
1038
1039 if (pcl816_check(iobase)) {
1040 rt_printk(", I cann't detect board. FAIL!\n");
1041 return -EIO;
1042 }
1043
Bill Pembertonfe0ff172009-03-16 22:18:43 -04001044 if ((ret = alloc_private(dev, sizeof(struct pcl816_private))) < 0)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001045 return ret; /* Can't alloc mem */
1046
1047 /* set up some name stuff */
1048 dev->board_name = this_board->name;
1049
1050 /* grab our IRQ */
1051 irq = 0;
1052 if (this_board->IRQbits != 0) { /* board support IRQ */
1053 irq = it->options[1];
1054 if (irq) { /* we want to use IRQ */
1055 if (((1 << irq) & this_board->IRQbits) == 0) {
1056 rt_printk
1057 (", IRQ %u is out of allowed range, DISABLING IT",
1058 irq);
1059 irq = 0; /* Bad IRQ */
1060 } else {
1061 if (comedi_request_irq(irq, interrupt_pcl816, 0,
1062 "pcl816", dev)) {
1063 rt_printk
1064 (", unable to allocate IRQ %u, DISABLING IT",
1065 irq);
1066 irq = 0; /* Can't use IRQ */
1067 } else {
1068 rt_printk(", irq=%u", irq);
1069 }
1070 }
1071 }
1072 }
1073
1074 dev->irq = irq;
1075 if (irq) {
1076 devpriv->irq_free = 1;
1077 } /* 1=we have allocated irq */
1078 else {
1079 devpriv->irq_free = 0;
1080 }
1081 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1082 devpriv->int816_mode = 0; /* mode of irq */
1083
1084#ifdef unused
1085 /* grab RTC for DMA operations */
1086 devpriv->dma_rtc = 0;
Bill Pemberton58c05762009-03-27 11:31:22 -04001087 if (it->options[2] > 0) { /* we want to use DMA */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001088 if (RTC_lock == 0) {
1089 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1090 "pcl816 (RTC)"))
1091 goto no_rtc;
1092 }
1093 devpriv->rtc_iobase = RTC_PORT(0);
1094 devpriv->rtc_iosize = RTC_IO_EXTENT;
1095 RTC_lock++;
1096#ifdef UNTESTED_CODE
1097 if (!comedi_request_irq(RTC_IRQ,
1098 interrupt_pcl816_ai_mode13_dma_rtc, 0,
1099 "pcl816 DMA (RTC)", dev)) {
1100 devpriv->dma_rtc = 1;
1101 devpriv->rtc_irq = RTC_IRQ;
1102 rt_printk(", dma_irq=%u", devpriv->rtc_irq);
1103 } else {
1104 RTC_lock--;
1105 if (RTC_lock == 0) {
1106 if (devpriv->rtc_iobase)
1107 release_region(devpriv->rtc_iobase,
1108 devpriv->rtc_iosize);
1109 }
1110 devpriv->rtc_iobase = 0;
1111 devpriv->rtc_iosize = 0;
1112 }
1113#else
1114 printk("pcl816: RTC code missing");
1115#endif
1116
1117 }
1118
1119 no_rtc:
1120#endif
1121 /* grab our DMA */
1122 dma = 0;
1123 devpriv->dma = dma;
1124 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1125 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1126
1127 if (this_board->DMAbits != 0) { /* board support DMA */
1128 dma = it->options[2];
1129 if (dma < 1)
1130 goto no_dma; /* DMA disabled */
1131
1132 if (((1 << dma) & this_board->DMAbits) == 0) {
1133 rt_printk(", DMA is out of allowed range, FAIL!\n");
1134 return -EINVAL; /* Bad DMA */
1135 }
1136 ret = request_dma(dma, "pcl816");
1137 if (ret) {
1138 rt_printk(", unable to allocate DMA %u, FAIL!\n", dma);
1139 return -EBUSY; /* DMA isn't free */
1140 }
1141
1142 devpriv->dma = dma;
1143 rt_printk(", dma=%u", dma);
1144 pages = 2; /* we need 16KB */
1145 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1146
1147 if (!devpriv->dmabuf[0]) {
1148 rt_printk(", unable to allocate DMA buffer, FAIL!\n");
1149 /* maybe experiment with try_to_free_pages() will help .... */
1150 return -EBUSY; /* no buffer :-( */
1151 }
1152 devpriv->dmapages[0] = pages;
1153 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1154 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
Bill Pemberton58c05762009-03-27 11:31:22 -04001155 /* rt_printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001156
Bill Pemberton58c05762009-03-27 11:31:22 -04001157 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001158 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1159 if (!devpriv->dmabuf[1]) {
1160 rt_printk
1161 (", unable to allocate DMA buffer, FAIL!\n");
1162 return -EBUSY;
1163 }
1164 devpriv->dmapages[1] = pages;
1165 devpriv->hwdmaptr[1] =
1166 virt_to_bus((void *)devpriv->dmabuf[1]);
1167 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1168 }
1169 }
1170
1171 no_dma:
1172
1173/* if (this_board->n_aochan > 0)
1174 subdevs[1] = COMEDI_SUBD_AO;
1175 if (this_board->n_dichan > 0)
1176 subdevs[2] = COMEDI_SUBD_DI;
1177 if (this_board->n_dochan > 0)
1178 subdevs[3] = COMEDI_SUBD_DO;
1179*/
1180 if ((ret = alloc_subdevices(dev, 1)) < 0)
1181 return ret;
1182
1183 s = dev->subdevices + 0;
1184 if (this_board->n_aichan > 0) {
1185 s->type = COMEDI_SUBD_AI;
1186 devpriv->sub_ai = s;
1187 dev->read_subdev = s;
1188 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1189 s->n_chan = this_board->n_aichan;
1190 s->subdev_flags |= SDF_DIFF;
Bill Pemberton58c05762009-03-27 11:31:22 -04001191 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001192 s->maxdata = this_board->ai_maxdata;
1193 s->len_chanlist = this_board->ai_chanlist;
1194 s->range_table = this_board->ai_range_type;
1195 s->cancel = pcl816_ai_cancel;
1196 s->do_cmdtest = pcl816_ai_cmdtest;
1197 s->do_cmd = pcl816_ai_cmd;
1198 s->poll = pcl816_ai_poll;
1199 s->insn_read = pcl816_ai_insn_read;
1200 } else {
1201 s->type = COMEDI_SUBD_UNUSED;
1202 }
1203
1204#if 0
1205case COMEDI_SUBD_AO:
1206 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1207 s->n_chan = this_board->n_aochan;
1208 s->maxdata = this_board->ao_maxdata;
1209 s->len_chanlist = this_board->ao_chanlist;
1210 s->range_table = this_board->ao_range_type;
1211 break;
1212
1213case COMEDI_SUBD_DI:
1214 s->subdev_flags = SDF_READABLE;
1215 s->n_chan = this_board->n_dichan;
1216 s->maxdata = 1;
1217 s->len_chanlist = this_board->n_dichan;
1218 s->range_table = &range_digital;
1219 break;
1220
1221case COMEDI_SUBD_DO:
1222 s->subdev_flags = SDF_WRITABLE;
1223 s->n_chan = this_board->n_dochan;
1224 s->maxdata = 1;
1225 s->len_chanlist = this_board->n_dochan;
1226 s->range_table = &range_digital;
1227 break;
1228#endif
1229
1230 pcl816_reset(dev);
1231
1232 rt_printk("\n");
1233
1234 return 0;
1235}
1236
1237/*
1238==============================================================================
1239 Removes device
1240 */
Bill Pemberton71b5f4f2009-03-16 22:05:08 -04001241static int pcl816_detach(struct comedi_device * dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001242{
1243 DEBUG(rt_printk("comedi%d: pcl816: remove\n", dev->minor);
1244 )
1245 free_resources(dev);
1246#ifdef unused
1247 if (devpriv->dma_rtc)
1248 RTC_lock--;
1249#endif
1250 return 0;
1251}