blob: 5c88ddc1fc0574ffc431fac058e02658d5bf7936 [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, {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053094 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 }
Juan Grigeradd2996b2009-02-19 09:30:57 -0800103};
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530104
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400105struct pcl816_board {
106
Bill Pemberton58c05762009-03-27 11:31:22 -0400107 const char *name; /* board name */
108 int n_ranges; /* len of range list */
109 int n_aichan; /* num of A/D chans in diferencial mode */
110 unsigned int ai_ns_min; /* minimal alllowed delay between samples (in ns) */
111 int n_aochan; /* num of D/A chans */
112 int n_dichan; /* num of DI chans */
113 int n_dochan; /* num of DO chans */
114 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
André Goddard Rosabbc9a992009-11-14 13:09:06 -0200115 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
Bill Pemberton58c05762009-03-27 11:31:22 -0400116 unsigned int io_range; /* len of IO space */
117 unsigned int IRQbits; /* allowed interrupts */
118 unsigned int DMAbits; /* allowed DMA chans */
119 int ai_maxdata; /* maxdata for A/D */
120 int ao_maxdata; /* maxdata for D/A */
121 int ai_chanlist; /* allowed len of channel list A/D */
122 int ao_chanlist; /* allowed len of channel list D/A */
123 int i8254_osc_base; /* 1/frequency of on board oscilator in ns */
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400124};
Juan Grigeradd2996b2009-02-19 09:30:57 -0800125
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400126static const struct pcl816_board boardtypes[] = {
Juan Grigeradd2996b2009-02-19 09:30:57 -0800127 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530128 &range_pcl816, PCLx1x_RANGE,
129 0x00fc, /* IRQ mask */
130 0x0a, /* DMA mask */
131 0xffff, /* 16-bit card */
132 0xffff, /* D/A maxdata */
133 1024,
134 1, /* ao chan list */
135 100},
Juan Grigeradd2996b2009-02-19 09:30:57 -0800136 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530137 &range_pcl816, PCLx1x_RANGE,
138 0x00fc,
139 0x0a,
140 0x3fff, /* 14 bit card */
141 0x3fff,
142 1024,
143 1,
144 100},
Juan Grigeradd2996b2009-02-19 09:30:57 -0800145};
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
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530151static int pcl816_attach(struct comedi_device *dev,
152 struct comedi_devconfig *it);
Bill Pembertonda91b262009-04-09 16:07:03 -0400153static int pcl816_detach(struct comedi_device *dev);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800154
155#ifdef unused
156static int RTC_lock = 0; /* RTC lock */
157static int RTC_timer_lock = 0; /* RTC int lock */
158#endif
159
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400160static struct comedi_driver driver_pcl816 = {
Bill Pemberton68c3dbf2009-04-22 21:11:49 -0400161 .driver_name = "pcl816",
162 .module = THIS_MODULE,
163 .attach = pcl816_attach,
164 .detach = pcl816_detach,
165 .board_name = &boardtypes[0].name,
166 .num_names = n_boardtypes,
167 .offset = sizeof(struct pcl816_board),
Juan Grigeradd2996b2009-02-19 09:30:57 -0800168};
169
170COMEDI_INITCLEANUP(driver_pcl816);
171
Bill Pembertonfe0ff172009-03-16 22:18:43 -0400172struct pcl816_private {
173
Bill Pemberton58c05762009-03-27 11:31:22 -0400174 unsigned int dma; /* used DMA, 0=don't use DMA */
175 int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800176#ifdef unused
Bill Pemberton58c05762009-03-27 11:31:22 -0400177 unsigned long rtc_iobase; /* RTC port region */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800178 unsigned int rtc_iosize;
179 unsigned int rtc_irq;
180#endif
Bill Pemberton58c05762009-03-27 11:31:22 -0400181 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
182 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
183 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
184 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
185 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
186 unsigned int last_top_dma; /* DMA pointer in last RTC int */
187 int next_dma_buf; /* which DMA buffer will be used next round */
188 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
189 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800190
Bill Pemberton58c05762009-03-27 11:31:22 -0400191 unsigned int ai_scans; /* len of scanlist */
192 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
193 int irq_free; /* 1=have allocated IRQ */
194 int irq_blocked; /* 1=IRQ now uses any subdev */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800195#ifdef unused
Bill Pemberton58c05762009-03-27 11:31:22 -0400196 int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800197#endif
Bill Pemberton58c05762009-03-27 11:31:22 -0400198 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
199 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
200 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
201 int ai_act_scan; /* how many scans we finished */
202 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
203 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
204 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
Ian Abbott13de4f02010-01-20 13:04:45 +0000205 unsigned int ai_n_chan; /* how many channels per scan */
Bill Pemberton58c05762009-03-27 11:31:22 -0400206 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
207 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800208#ifdef unused
Bill Pemberton58c05762009-03-27 11:31:22 -0400209 struct timer_list rtc_irq_timer; /* timer for RTC sanity check */
210 unsigned long rtc_freq; /* RTC int freq */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800211#endif
Bill Pembertonfe0ff172009-03-16 22:18:43 -0400212};
213
Juan Grigeradd2996b2009-02-19 09:30:57 -0800214/*
215==============================================================================
216*/
Bill Pembertonda91b262009-04-09 16:07:03 -0400217static int check_and_setup_channel_list(struct comedi_device *dev,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530218 struct comedi_subdevice *s,
219 unsigned int *chanlist, int chanlen);
220static int pcl816_ai_cancel(struct comedi_device *dev,
221 struct comedi_subdevice *s);
222static void start_pacer(struct comedi_device *dev, int mode,
223 unsigned int divisor1, unsigned int divisor2);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800224#ifdef unused
225static int set_rtc_irq_bit(unsigned char bit);
226#endif
227
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530228static int pcl816_ai_cmdtest(struct comedi_device *dev,
229 struct comedi_subdevice *s,
230 struct comedi_cmd *cmd);
Bill Pembertonda91b262009-04-09 16:07:03 -0400231static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800232
233/*
234==============================================================================
235 ANALOG INPUT MODE0, 816 cards, slow version
236*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530237static int pcl816_ai_insn_read(struct comedi_device *dev,
238 struct comedi_subdevice *s,
239 struct comedi_insn *insn, unsigned int *data)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800240{
241 int n;
242 int timeout;
243
244 DPRINTK("mode 0 analog input\n");
Bill Pemberton58c05762009-03-27 11:31:22 -0400245 /* software trigger, DMA and INT off */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800246 outb(0, dev->iobase + PCL816_CONTROL);
Bill Pemberton58c05762009-03-27 11:31:22 -0400247 /* clear INT (conversion end) flag */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800248 outb(0, dev->iobase + PCL816_CLRINT);
249
Bill Pemberton58c05762009-03-27 11:31:22 -0400250 /* Set the input channel */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800251 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
252 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */
253
254 for (n = 0; n < insn->n; n++) {
255
256 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
257
258 timeout = 100;
259 while (timeout--) {
260 if (!(inb(dev->iobase + PCL816_STATUS) &
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530261 PCL816_STATUS_DRDY_MASK)) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400262 /* return read value */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800263 data[n] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530264 ((inb(dev->iobase +
265 PCL816_AD_HI) << 8) |
266 (inb(dev->iobase + PCL816_AD_LO)));
Juan Grigeradd2996b2009-02-19 09:30:57 -0800267
268 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
269 break;
270 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700271 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800272 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400273 /* Return timeout error */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800274 if (!timeout) {
275 comedi_error(dev, "A/D insn timeout\n");
276 data[0] = 0;
277 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
278 return -EIO;
279 }
280
281 }
282 return n;
283}
284
285/*
286==============================================================================
287 analog input interrupt mode 1 & 3, 818 cards
288 one sample per interrupt version
289*/
290static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
291{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400292 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400293 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800294 int low, hi;
295 int timeout = 50; /* wait max 50us */
296
297 while (timeout--) {
298 if (!(inb(dev->iobase + PCL816_STATUS) &
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530299 PCL816_STATUS_DRDY_MASK))
Juan Grigeradd2996b2009-02-19 09:30:57 -0800300 break;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700301 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800302 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400303 if (!timeout) { /* timeout, bail error */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800304 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
305 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
306 pcl816_ai_cancel(dev, s);
307 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
308 comedi_event(dev, s);
309 return IRQ_HANDLED;
310
311 }
312
Bill Pemberton58c05762009-03-27 11:31:22 -0400313 /* get the sample */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800314 low = inb(dev->iobase + PCL816_AD_LO);
315 hi = inb(dev->iobase + PCL816_AD_HI);
316
317 comedi_buf_put(s->async, (hi << 8) | low);
318
319 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
320
321 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
322 devpriv->ai_act_chanlist_pos = 0;
323
Ian Abbott13de4f02010-01-20 13:04:45 +0000324 s->async->cur_chan++;
325 if (s->async->cur_chan >= devpriv->ai_n_chan) {
326 s->async->cur_chan = 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800327 devpriv->ai_act_scan++;
328 }
329
330 if (!devpriv->ai_neverending)
331 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
332 /* all data sampled */
333 pcl816_ai_cancel(dev, s);
334 s->async->events |= COMEDI_CB_EOA;
335 }
336 comedi_event(dev, s);
337 return IRQ_HANDLED;
338}
339
340/*
341==============================================================================
342 analog input dma mode 1 & 3, 816 cards
343*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530344static void transfer_from_dma_buf(struct comedi_device *dev,
345 struct comedi_subdevice *s, short *ptr,
346 unsigned int bufptr, unsigned int len)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800347{
348 int i;
349
350 s->async->events = 0;
351
352 for (i = 0; i < len; i++) {
353
354 comedi_buf_put(s->async, ptr[bufptr++]);
355
356 if (++devpriv->ai_act_chanlist_pos >=
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530357 devpriv->ai_act_chanlist_len) {
Juan Grigeradd2996b2009-02-19 09:30:57 -0800358 devpriv->ai_act_chanlist_pos = 0;
Ian Abbott13de4f02010-01-20 13:04:45 +0000359 }
360
361 s->async->cur_chan++;
362 if (s->async->cur_chan >= devpriv->ai_n_chan) {
363 s->async->cur_chan = 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800364 devpriv->ai_act_scan++;
365 }
366
367 if (!devpriv->ai_neverending)
Bill Pemberton58c05762009-03-27 11:31:22 -0400368 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800369 pcl816_ai_cancel(dev, s);
370 s->async->events |= COMEDI_CB_EOA;
371 s->async->events |= COMEDI_CB_BLOCK;
372 break;
373 }
374 }
375
376 comedi_event(dev, s);
377}
378
379static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
380{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400381 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400382 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800383 int len, bufptr, this_dma_buf;
384 unsigned long dma_flags;
Bill Pemberton790c5542009-03-16 22:05:02 -0400385 short *ptr;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800386
387 disable_dma(devpriv->dma);
388 this_dma_buf = devpriv->next_dma_buf;
389
Bill Pemberton58c05762009-03-27 11:31:22 -0400390 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { /* switch dma bufs */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800391
392 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
393 set_dma_mode(devpriv->dma, DMA_MODE_READ);
394 dma_flags = claim_dma_lock();
Bill Pemberton58c05762009-03-27 11:31:22 -0400395/* clear_dma_ff (devpriv->dma); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800396 set_dma_addr(devpriv->dma,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530397 devpriv->hwdmaptr[devpriv->next_dma_buf]);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800398 if (devpriv->dma_runs_to_end) {
399 set_dma_count(devpriv->dma,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530400 devpriv->hwdmasize[devpriv->
401 next_dma_buf]);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800402 } else {
403 set_dma_count(devpriv->dma, devpriv->last_dma_run);
404 }
405 release_dma_lock(dma_flags);
406 enable_dma(devpriv->dma);
407 }
408
409 devpriv->dma_runs_to_end--;
410 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
411
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530412 ptr = (short *)devpriv->dmabuf[this_dma_buf];
Juan Grigeradd2996b2009-02-19 09:30:57 -0800413
414 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
415 bufptr = devpriv->ai_poll_ptr;
416 devpriv->ai_poll_ptr = 0;
417
418 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
419 return IRQ_HANDLED;
420}
421
422/*
423==============================================================================
424 INT procedure
425*/
Jiri Slaby70265d22009-03-26 09:34:06 +0100426static irqreturn_t interrupt_pcl816(int irq, void *d)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800427{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400428 struct comedi_device *dev = d;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800429 DPRINTK("<I>");
430
431 if (!dev->attached) {
432 comedi_error(dev, "premature interrupt");
433 return IRQ_HANDLED;
434 }
435
436 switch (devpriv->int816_mode) {
437 case INT_TYPE_AI1_DMA:
438 case INT_TYPE_AI3_DMA:
439 return interrupt_pcl816_ai_mode13_dma(irq, d);
440 case INT_TYPE_AI1_INT:
441 case INT_TYPE_AI3_INT:
442 return interrupt_pcl816_ai_mode13_int(irq, d);
443 }
444
445 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
446 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530447 (!devpriv->int816_mode)) {
Juan Grigeradd2996b2009-02-19 09:30:57 -0800448 if (devpriv->irq_was_now_closed) {
449 devpriv->irq_was_now_closed = 0;
Bill Pemberton58c05762009-03-27 11:31:22 -0400450 /* comedi_error(dev,"last IRQ.."); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800451 return IRQ_HANDLED;
452 }
453 comedi_error(dev, "bad IRQ!");
454 return IRQ_NONE;
455 }
André Goddard Rosabbc9a992009-11-14 13:09:06 -0200456 comedi_error(dev, "IRQ from unknown source!");
Juan Grigeradd2996b2009-02-19 09:30:57 -0800457 return IRQ_NONE;
458}
459
460/*
461==============================================================================
462 COMMAND MODE
463*/
Bill Pembertonda91b262009-04-09 16:07:03 -0400464static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800465{
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700466 printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530467 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700468 printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530469 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700470 printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530471 cmd->scan_end_src);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700472 printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530473 cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800474}
475
476/*
477==============================================================================
478*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530479static int pcl816_ai_cmdtest(struct comedi_device *dev,
480 struct comedi_subdevice *s, struct comedi_cmd *cmd)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800481{
482 int err = 0;
Ian Abbott48b1aff2009-11-20 11:32:37 +0000483 int tmp, divisor1 = 0, divisor2 = 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800484
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530485 DEBUG(printk("pcl816 pcl812_ai_cmdtest\n"); pcl816_cmdtest_out(-1, cmd);
486 );
Juan Grigeradd2996b2009-02-19 09:30:57 -0800487
488 /* step 1: make sure trigger sources are trivially valid */
489 tmp = cmd->start_src;
490 cmd->start_src &= TRIG_NOW;
491 if (!cmd->start_src || tmp != cmd->start_src)
492 err++;
493
494 tmp = cmd->scan_begin_src;
495 cmd->scan_begin_src &= TRIG_FOLLOW;
496 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
497 err++;
498
Ian Abbottefdf83c2009-11-20 11:32:38 +0000499 tmp = cmd->convert_src;
500 cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
501 if (!cmd->convert_src || tmp != cmd->convert_src)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800502 err++;
503
504 tmp = cmd->scan_end_src;
505 cmd->scan_end_src &= TRIG_COUNT;
506 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
507 err++;
508
509 tmp = cmd->stop_src;
510 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
511 if (!cmd->stop_src || tmp != cmd->stop_src)
512 err++;
513
514 if (err) {
515 return 1;
516 }
517
518 /* step 2: make sure trigger sources are unique and mutually compatible */
519
520 if (cmd->start_src != TRIG_NOW) {
521 cmd->start_src = TRIG_NOW;
522 err++;
523 }
524
525 if (cmd->scan_begin_src != TRIG_FOLLOW) {
526 cmd->scan_begin_src = TRIG_FOLLOW;
527 err++;
528 }
529
530 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
531 cmd->convert_src = TRIG_TIMER;
532 err++;
533 }
534
535 if (cmd->scan_end_src != TRIG_COUNT) {
536 cmd->scan_end_src = TRIG_COUNT;
537 err++;
538 }
539
540 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
541 err++;
542
543 if (err) {
544 return 2;
545 }
546
547 /* step 3: make sure arguments are trivially compatible */
548 if (cmd->start_arg != 0) {
549 cmd->start_arg = 0;
550 err++;
551 }
552
553 if (cmd->scan_begin_arg != 0) {
554 cmd->scan_begin_arg = 0;
555 err++;
556 }
557 if (cmd->convert_src == TRIG_TIMER) {
558 if (cmd->convert_arg < this_board->ai_ns_min) {
559 cmd->convert_arg = this_board->ai_ns_min;
560 err++;
561 }
562 } else { /* TRIG_EXT */
563 if (cmd->convert_arg != 0) {
564 cmd->convert_arg = 0;
565 err++;
566 }
567 }
568
569 if (!cmd->chanlist_len) {
570 cmd->chanlist_len = 1;
571 err++;
572 }
573 if (cmd->chanlist_len > this_board->n_aichan) {
574 cmd->chanlist_len = this_board->n_aichan;
575 err++;
576 }
577 if (cmd->scan_end_arg != cmd->chanlist_len) {
578 cmd->scan_end_arg = cmd->chanlist_len;
579 err++;
580 }
581 if (cmd->stop_src == TRIG_COUNT) {
582 if (!cmd->stop_arg) {
583 cmd->stop_arg = 1;
584 err++;
585 }
586 } else { /* TRIG_NONE */
587 if (cmd->stop_arg != 0) {
588 cmd->stop_arg = 0;
589 err++;
590 }
591 }
592
593 if (err) {
594 return 3;
595 }
596
597 /* step 4: fix up any arguments */
598 if (cmd->convert_src == TRIG_TIMER) {
599 tmp = cmd->convert_arg;
600 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530601 &divisor1, &divisor2,
602 &cmd->convert_arg,
603 cmd->flags & TRIG_ROUND_MASK);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800604 if (cmd->convert_arg < this_board->ai_ns_min)
605 cmd->convert_arg = this_board->ai_ns_min;
606 if (tmp != cmd->convert_arg)
607 err++;
608 }
609
610 if (err) {
611 return 4;
612 }
613
614 return 0;
615}
616
Bill Pembertonda91b262009-04-09 16:07:03 -0400617static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800618{
619 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400620 struct comedi_cmd *cmd = &s->async->cmd;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800621
622 if (cmd->start_src != TRIG_NOW)
623 return -EINVAL;
624 if (cmd->scan_begin_src != TRIG_FOLLOW)
625 return -EINVAL;
626 if (cmd->scan_end_src != TRIG_COUNT)
627 return -EINVAL;
628 if (cmd->scan_end_arg != cmd->chanlist_len)
629 return -EINVAL;
Bill Pemberton58c05762009-03-27 11:31:22 -0400630/* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800631 if (devpriv->irq_blocked)
632 return -EBUSY;
633
634 if (cmd->convert_src == TRIG_TIMER) {
635 if (cmd->convert_arg < this_board->ai_ns_min)
636 cmd->convert_arg = this_board->ai_ns_min;
637
638 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530639 &divisor2, &cmd->convert_arg,
640 cmd->flags & TRIG_ROUND_MASK);
Bill Pemberton58c05762009-03-27 11:31:22 -0400641 if (divisor1 == 1) { /* PCL816 crash if any divisor is set to 1 */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800642 divisor1 = 2;
643 divisor2 /= 2;
644 }
645 if (divisor2 == 1) {
646 divisor2 = 2;
647 divisor1 /= 2;
648 }
649 }
650
Bill Pemberton58c05762009-03-27 11:31:22 -0400651 start_pacer(dev, -1, 0, 0); /* stop pacer */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800652
653 if (!check_and_setup_channel_list(dev, s, cmd->chanlist,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530654 cmd->chanlist_len))
Juan Grigeradd2996b2009-02-19 09:30:57 -0800655 return -EINVAL;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700656 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800657
Ian Abbott13de4f02010-01-20 13:04:45 +0000658 devpriv->ai_n_chan = cmd->chanlist_len;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800659 devpriv->ai_act_scan = 0;
660 s->async->cur_chan = 0;
661 devpriv->irq_blocked = 1;
662 devpriv->ai_poll_ptr = 0;
663 devpriv->irq_was_now_closed = 0;
664
665 if (cmd->stop_src == TRIG_COUNT) {
666 devpriv->ai_scans = cmd->stop_arg;
667 devpriv->ai_neverending = 0;
668 } else {
669 devpriv->ai_scans = 0;
670 devpriv->ai_neverending = 1;
671 }
672
Bill Pemberton58c05762009-03-27 11:31:22 -0400673 if ((cmd->flags & TRIG_WAKE_EOS)) { /* don't we want wake up every scan? */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800674 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
Bill Pemberton58c05762009-03-27 11:31:22 -0400675 /* devpriv->ai_eos=1; */
676 /* if (devpriv->ai_n_chan==1) */
677 /* devpriv->dma=0; // DMA is useless for this situation */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800678 }
679
680 if (devpriv->dma) {
681 bytes = devpriv->hwdmasize[0];
682 if (!devpriv->ai_neverending) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400683 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); /* how many */
684 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fill */
685 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800686 devpriv->dma_runs_to_end--;
687 if (devpriv->dma_runs_to_end >= 0)
688 bytes = devpriv->hwdmasize[0];
689 } else
690 devpriv->dma_runs_to_end = -1;
691
692 devpriv->next_dma_buf = 0;
693 set_dma_mode(devpriv->dma, DMA_MODE_READ);
694 dma_flags = claim_dma_lock();
695 clear_dma_ff(devpriv->dma);
696 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
697 set_dma_count(devpriv->dma, bytes);
698 release_dma_lock(dma_flags);
699 enable_dma(devpriv->dma);
700 }
701
702 start_pacer(dev, 1, divisor1, divisor2);
703 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
704
705 switch (cmd->convert_src) {
706 case TRIG_TIMER:
707 devpriv->int816_mode = INT_TYPE_AI1_DMA;
Bill Pemberton58c05762009-03-27 11:31:22 -0400708 outb(0x32, dev->iobase + PCL816_CONTROL); /* Pacer+IRQ+DMA */
709 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq and DMA to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800710 break;
711
712 default:
713 devpriv->int816_mode = INT_TYPE_AI3_DMA;
Bill Pemberton58c05762009-03-27 11:31:22 -0400714 outb(0x34, dev->iobase + PCL816_CONTROL); /* Ext trig+IRQ+DMA */
715 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800716 break;
717 }
718
719 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
720 return 0;
721}
722
Bill Pembertonda91b262009-04-09 16:07:03 -0400723static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800724{
725 unsigned long flags;
726 unsigned int top1, top2, i;
727
728 if (!devpriv->dma)
Bill Pemberton58c05762009-03-27 11:31:22 -0400729 return 0; /* poll is valid only for DMA transfer */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800730
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700731 spin_lock_irqsave(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800732
733 for (i = 0; i < 20; i++) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400734 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800735 top2 = get_dma_residue(devpriv->dma);
736 if (top1 == top2)
737 break;
738 }
739 if (top1 != top2) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700740 spin_unlock_irqrestore(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800741 return 0;
742 }
743
Bill Pemberton58c05762009-03-27 11:31:22 -0400744 top1 = devpriv->hwdmasize[0] - top1; /* where is now DMA in buffer */
745 top1 >>= 1; /* sample position */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800746 top2 = top1 - devpriv->ai_poll_ptr;
Bill Pemberton58c05762009-03-27 11:31:22 -0400747 if (top2 < 1) { /* no new samples */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700748 spin_unlock_irqrestore(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800749 return 0;
750 }
751
752 transfer_from_dma_buf(dev, s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530753 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
754 devpriv->ai_poll_ptr, top2);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800755
Bill Pemberton58c05762009-03-27 11:31:22 -0400756 devpriv->ai_poll_ptr = top1; /* new buffer position */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700757 spin_unlock_irqrestore(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800758
759 return s->async->buf_write_count - s->async->buf_read_count;
760}
761
762/*
763==============================================================================
764 cancel any mode 1-4 AI
765*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530766static int pcl816_ai_cancel(struct comedi_device *dev,
767 struct comedi_subdevice *s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800768{
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700769/* DEBUG(printk("pcl816_ai_cancel()\n");) */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800770
771 if (devpriv->irq_blocked > 0) {
772 switch (devpriv->int816_mode) {
773#ifdef unused
774 case INT_TYPE_AI1_DMA_RTC:
775 case INT_TYPE_AI3_DMA_RTC:
Bill Pemberton58c05762009-03-27 11:31:22 -0400776 set_rtc_irq_bit(0); /* stop RTC */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800777 del_timer(&devpriv->rtc_irq_timer);
778#endif
779 case INT_TYPE_AI1_DMA:
780 case INT_TYPE_AI3_DMA:
781 disable_dma(devpriv->dma);
782 case INT_TYPE_AI1_INT:
783 case INT_TYPE_AI3_INT:
784 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700785 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800786 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
787 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
788 outb(0x70, dev->iobase + PCL816_CTRCTL);
789 outb(0, dev->iobase + PCL816_AD_LO);
790 inb(dev->iobase + PCL816_AD_LO);
791 inb(dev->iobase + PCL816_AD_HI);
792 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
793 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
794 devpriv->irq_blocked = 0;
795 devpriv->irq_was_now_closed = devpriv->int816_mode;
796 devpriv->int816_mode = 0;
797 devpriv->last_int_sub = s;
Bill Pemberton58c05762009-03-27 11:31:22 -0400798/* s->busy = 0; */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800799 break;
800 }
801 }
802
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530803 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
804 return 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800805}
806
807/*
808==============================================================================
809 chech for PCL816
810*/
811static int pcl816_check(unsigned long iobase)
812{
813 outb(0x00, iobase + PCL816_MUX);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700814 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800815 if (inb(iobase + PCL816_MUX) != 0x00)
Bill Pemberton58c05762009-03-27 11:31:22 -0400816 return 1; /* there isn't card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800817 outb(0x55, iobase + PCL816_MUX);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700818 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800819 if (inb(iobase + PCL816_MUX) != 0x55)
Bill Pemberton58c05762009-03-27 11:31:22 -0400820 return 1; /* there isn't card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800821 outb(0x00, iobase + PCL816_MUX);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700822 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800823 outb(0x18, iobase + PCL816_CONTROL);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700824 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800825 if (inb(iobase + PCL816_CONTROL) != 0x18)
Bill Pemberton58c05762009-03-27 11:31:22 -0400826 return 1; /* there isn't card */
827 return 0; /* ok, card exist */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800828}
829
830/*
831==============================================================================
832 reset whole PCL-816 cards
833*/
Bill Pembertonda91b262009-04-09 16:07:03 -0400834static void pcl816_reset(struct comedi_device *dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800835{
Bill Pemberton58c05762009-03-27 11:31:22 -0400836/* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
837/* outb (0, dev->iobase + PCL818_DA_HI); */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700838/* udelay (1); */
Bill Pemberton58c05762009-03-27 11:31:22 -0400839/* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
840/* outb (0, dev->iobase + PCL818_DO_LO); */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700841/* udelay (1); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800842 outb(0, dev->iobase + PCL816_CONTROL);
843 outb(0, dev->iobase + PCL816_MUX);
844 outb(0, dev->iobase + PCL816_CLRINT);
845 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
846 outb(0x70, dev->iobase + PCL816_CTRCTL);
847 outb(0x30, dev->iobase + PCL816_CTRCTL);
848 outb(0, dev->iobase + PCL816_RANGE);
849}
850
851/*
852==============================================================================
853 Start/stop pacer onboard pacer
854*/
855static void
Bill Pembertonda91b262009-04-09 16:07:03 -0400856start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530857 unsigned int divisor2)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800858{
859 outb(0x32, dev->iobase + PCL816_CTRCTL);
860 outb(0xff, dev->iobase + PCL816_CTR0);
861 outb(0x00, dev->iobase + PCL816_CTR0);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700862 udelay(1);
Bill Pemberton58c05762009-03-27 11:31:22 -0400863 outb(0xb4, dev->iobase + PCL816_CTRCTL); /* set counter 2 as mode 3 */
864 outb(0x74, dev->iobase + PCL816_CTRCTL); /* set counter 1 as mode 3 */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700865 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800866
867 if (mode == 1) {
868 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
869 divisor2);
870 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
871 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
872 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
873 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
874 }
875
876 /* clear pending interrupts (just in case) */
Bill Pemberton58c05762009-03-27 11:31:22 -0400877/* outb(0, dev->iobase + PCL816_CLRINT); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800878}
879
880/*
881==============================================================================
882 Check if channel list from user is builded correctly
883 If it's ok, then program scan/gain logic
884*/
885static int
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530886check_and_setup_channel_list(struct comedi_device *dev,
887 struct comedi_subdevice *s, unsigned int *chanlist,
888 int chanlen)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800889{
890 unsigned int chansegment[16];
891 unsigned int i, nowmustbechan, seglen, segpos;
892
Bill Pemberton58c05762009-03-27 11:31:22 -0400893 /* correct channel and range number check itself comedi/range.c */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800894 if (chanlen < 1) {
895 comedi_error(dev, "range/channel list is empty!");
896 return 0;
897 }
898
899 if (chanlen > 1) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400900 chansegment[0] = chanlist[0]; /* first channel is everytime ok */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800901 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400902 /* build part of chanlist */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700903 DEBUG(printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530904 CR_RANGE(chanlist[i]));)
905 if (chanlist[0] == chanlist[i])
Bill Pemberton58c05762009-03-27 11:31:22 -0400906 break; /* we detect loop, this must by finish */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800907 nowmustbechan =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530908 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800909 if (nowmustbechan != CR_CHAN(chanlist[i])) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400910 /* channel list isn't continous :-( */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700911 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530912 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
913 dev->minor, i, CR_CHAN(chanlist[i]),
914 nowmustbechan, CR_CHAN(chanlist[0]));
Juan Grigeradd2996b2009-02-19 09:30:57 -0800915 return 0;
916 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400917 chansegment[i] = chanlist[i]; /* well, this is next correct channel in list */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800918 }
919
Bill Pemberton58c05762009-03-27 11:31:22 -0400920 for (i = 0, segpos = 0; i < chanlen; i++) { /* check whole chanlist */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700921 DEBUG(printk("%d %d=%d %d\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530922 CR_CHAN(chansegment[i % seglen]),
923 CR_RANGE(chansegment[i % seglen]),
924 CR_CHAN(chanlist[i]),
925 CR_RANGE(chanlist[i]));)
926 if (chanlist[i] != chansegment[i % seglen]) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700927 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530928 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
929 dev->minor, i, CR_CHAN(chansegment[i]),
930 CR_RANGE(chansegment[i]),
931 CR_AREF(chansegment[i]),
932 CR_CHAN(chanlist[i % seglen]),
933 CR_RANGE(chanlist[i % seglen]),
934 CR_AREF(chansegment[i % seglen]));
Bill Pemberton58c05762009-03-27 11:31:22 -0400935 return 0; /* chan/gain list is strange */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800936 }
937 }
938 } else {
939 seglen = 1;
940 }
941
942 devpriv->ai_act_chanlist_len = seglen;
943 devpriv->ai_act_chanlist_pos = 0;
944
Bill Pemberton58c05762009-03-27 11:31:22 -0400945 for (i = 0; i < seglen; i++) { /* store range list to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800946 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
947 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
948 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */
949 }
950
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700951 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800952
953 outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */
954
Bill Pemberton58c05762009-03-27 11:31:22 -0400955 return 1; /* we can serve this with MUX logic */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800956}
957
958#ifdef unused
959/*
960==============================================================================
961 Enable(1)/disable(0) periodic interrupts from RTC
962*/
963static int set_rtc_irq_bit(unsigned char bit)
964{
965 unsigned char val;
966 unsigned long flags;
967
968 if (bit == 1) {
969 RTC_timer_lock++;
970 if (RTC_timer_lock > 1)
971 return 0;
972 } else {
973 RTC_timer_lock--;
974 if (RTC_timer_lock < 0)
975 RTC_timer_lock = 0;
976 if (RTC_timer_lock > 0)
977 return 0;
978 }
979
980 save_flags(flags);
981 cli();
982 val = CMOS_READ(RTC_CONTROL);
983 if (bit) {
984 val |= RTC_PIE;
985 } else {
986 val &= ~RTC_PIE;
987 }
988 CMOS_WRITE(val, RTC_CONTROL);
989 CMOS_READ(RTC_INTR_FLAGS);
990 restore_flags(flags);
991 return 0;
992}
993#endif
994
995/*
996==============================================================================
997 Free any resources that we have claimed
998*/
Bill Pembertonda91b262009-04-09 16:07:03 -0400999static void free_resources(struct comedi_device *dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001000{
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001001 /* printk("free_resource()\n"); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001002 if (dev->private) {
1003 pcl816_ai_cancel(dev, devpriv->sub_ai);
1004 pcl816_reset(dev);
1005 if (devpriv->dma)
1006 free_dma(devpriv->dma);
1007 if (devpriv->dmabuf[0])
1008 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1009 if (devpriv->dmabuf[1])
1010 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1011#ifdef unused
1012 if (devpriv->rtc_irq)
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001013 free_irq(devpriv->rtc_irq, dev);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001014 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1015 if (devpriv->rtc_iobase)
1016 release_region(devpriv->rtc_iobase,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301017 devpriv->rtc_iosize);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001018 }
1019#endif
1020 }
1021
1022 if (dev->irq)
1023 free_irq(dev->irq, dev);
1024 if (dev->iobase)
1025 release_region(dev->iobase, this_board->io_range);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001026 /* printk("free_resource() end\n"); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001027}
1028
1029/*
1030==============================================================================
1031
1032 Initialization
1033
1034*/
Bill Pembertonda91b262009-04-09 16:07:03 -04001035static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001036{
1037 int ret;
1038 unsigned long iobase;
1039 unsigned int irq, dma;
1040 unsigned long pages;
Bill Pemberton58c05762009-03-27 11:31:22 -04001041 /* int i; */
Bill Pemberton34c43922009-03-16 22:05:14 -04001042 struct comedi_subdevice *s;
Juan Grigeradd2996b2009-02-19 09:30:57 -08001043
1044 /* claim our I/O space */
1045 iobase = it->options[0];
1046 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301047 this_board->name, iobase);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001048
1049 if (!request_region(iobase, this_board->io_range, "pcl816")) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001050 printk("I/O port conflict\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001051 return -EIO;
1052 }
1053
1054 dev->iobase = iobase;
1055
1056 if (pcl816_check(iobase)) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001057 printk(", I cann't detect board. FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001058 return -EIO;
1059 }
1060
Bill Pembertonc3744132009-04-22 21:11:47 -04001061 ret = alloc_private(dev, sizeof(struct pcl816_private));
1062 if (ret < 0)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001063 return ret; /* Can't alloc mem */
1064
1065 /* set up some name stuff */
1066 dev->board_name = this_board->name;
1067
1068 /* grab our IRQ */
1069 irq = 0;
1070 if (this_board->IRQbits != 0) { /* board support IRQ */
1071 irq = it->options[1];
1072 if (irq) { /* we want to use IRQ */
1073 if (((1 << irq) & this_board->IRQbits) == 0) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001074 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301075 (", IRQ %u is out of allowed range, DISABLING IT",
1076 irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001077 irq = 0; /* Bad IRQ */
1078 } else {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301079 if (request_irq
1080 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001081 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301082 (", unable to allocate IRQ %u, DISABLING IT",
1083 irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001084 irq = 0; /* Can't use IRQ */
1085 } else {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001086 printk(", irq=%u", irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001087 }
1088 }
1089 }
1090 }
1091
1092 dev->irq = irq;
1093 if (irq) {
1094 devpriv->irq_free = 1;
1095 } /* 1=we have allocated irq */
1096 else {
1097 devpriv->irq_free = 0;
1098 }
1099 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1100 devpriv->int816_mode = 0; /* mode of irq */
1101
1102#ifdef unused
1103 /* grab RTC for DMA operations */
1104 devpriv->dma_rtc = 0;
Bill Pemberton58c05762009-03-27 11:31:22 -04001105 if (it->options[2] > 0) { /* we want to use DMA */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001106 if (RTC_lock == 0) {
1107 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301108 "pcl816 (RTC)"))
Juan Grigeradd2996b2009-02-19 09:30:57 -08001109 goto no_rtc;
1110 }
1111 devpriv->rtc_iobase = RTC_PORT(0);
1112 devpriv->rtc_iosize = RTC_IO_EXTENT;
1113 RTC_lock++;
1114#ifdef UNTESTED_CODE
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001115 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301116 "pcl816 DMA (RTC)", dev)) {
Juan Grigeradd2996b2009-02-19 09:30:57 -08001117 devpriv->dma_rtc = 1;
1118 devpriv->rtc_irq = RTC_IRQ;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001119 printk(", dma_irq=%u", devpriv->rtc_irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001120 } else {
1121 RTC_lock--;
1122 if (RTC_lock == 0) {
1123 if (devpriv->rtc_iobase)
1124 release_region(devpriv->rtc_iobase,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301125 devpriv->rtc_iosize);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001126 }
1127 devpriv->rtc_iobase = 0;
1128 devpriv->rtc_iosize = 0;
1129 }
1130#else
1131 printk("pcl816: RTC code missing");
1132#endif
1133
1134 }
1135
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301136no_rtc:
Juan Grigeradd2996b2009-02-19 09:30:57 -08001137#endif
1138 /* grab our DMA */
1139 dma = 0;
1140 devpriv->dma = dma;
1141 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1142 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1143
1144 if (this_board->DMAbits != 0) { /* board support DMA */
1145 dma = it->options[2];
1146 if (dma < 1)
1147 goto no_dma; /* DMA disabled */
1148
1149 if (((1 << dma) & this_board->DMAbits) == 0) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001150 printk(", DMA is out of allowed range, FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001151 return -EINVAL; /* Bad DMA */
1152 }
1153 ret = request_dma(dma, "pcl816");
1154 if (ret) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001155 printk(", unable to allocate DMA %u, FAIL!\n", dma);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001156 return -EBUSY; /* DMA isn't free */
1157 }
1158
1159 devpriv->dma = dma;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001160 printk(", dma=%u", dma);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001161 pages = 2; /* we need 16KB */
1162 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1163
1164 if (!devpriv->dmabuf[0]) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001165 printk(", unable to allocate DMA buffer, FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001166 /* maybe experiment with try_to_free_pages() will help .... */
1167 return -EBUSY; /* no buffer :-( */
1168 }
1169 devpriv->dmapages[0] = pages;
1170 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1171 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001172 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001173
Bill Pemberton58c05762009-03-27 11:31:22 -04001174 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001175 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1176 if (!devpriv->dmabuf[1]) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001177 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301178 (", unable to allocate DMA buffer, FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001179 return -EBUSY;
1180 }
1181 devpriv->dmapages[1] = pages;
1182 devpriv->hwdmaptr[1] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301183 virt_to_bus((void *)devpriv->dmabuf[1]);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001184 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1185 }
1186 }
1187
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301188no_dma:
Juan Grigeradd2996b2009-02-19 09:30:57 -08001189
1190/* if (this_board->n_aochan > 0)
1191 subdevs[1] = COMEDI_SUBD_AO;
1192 if (this_board->n_dichan > 0)
1193 subdevs[2] = COMEDI_SUBD_DI;
1194 if (this_board->n_dochan > 0)
1195 subdevs[3] = COMEDI_SUBD_DO;
1196*/
Bill Pembertonc3744132009-04-22 21:11:47 -04001197
1198 ret = alloc_subdevices(dev, 1);
1199 if (ret < 0)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001200 return ret;
1201
1202 s = dev->subdevices + 0;
1203 if (this_board->n_aichan > 0) {
1204 s->type = COMEDI_SUBD_AI;
1205 devpriv->sub_ai = s;
1206 dev->read_subdev = s;
1207 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1208 s->n_chan = this_board->n_aichan;
1209 s->subdev_flags |= SDF_DIFF;
Bill Pemberton58c05762009-03-27 11:31:22 -04001210 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001211 s->maxdata = this_board->ai_maxdata;
1212 s->len_chanlist = this_board->ai_chanlist;
1213 s->range_table = this_board->ai_range_type;
1214 s->cancel = pcl816_ai_cancel;
1215 s->do_cmdtest = pcl816_ai_cmdtest;
1216 s->do_cmd = pcl816_ai_cmd;
1217 s->poll = pcl816_ai_poll;
1218 s->insn_read = pcl816_ai_insn_read;
1219 } else {
1220 s->type = COMEDI_SUBD_UNUSED;
1221 }
1222
1223#if 0
1224case COMEDI_SUBD_AO:
1225 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1226 s->n_chan = this_board->n_aochan;
1227 s->maxdata = this_board->ao_maxdata;
1228 s->len_chanlist = this_board->ao_chanlist;
1229 s->range_table = this_board->ao_range_type;
1230 break;
1231
1232case COMEDI_SUBD_DI:
1233 s->subdev_flags = SDF_READABLE;
1234 s->n_chan = this_board->n_dichan;
1235 s->maxdata = 1;
1236 s->len_chanlist = this_board->n_dichan;
1237 s->range_table = &range_digital;
1238 break;
1239
1240case COMEDI_SUBD_DO:
1241 s->subdev_flags = SDF_WRITABLE;
1242 s->n_chan = this_board->n_dochan;
1243 s->maxdata = 1;
1244 s->len_chanlist = this_board->n_dochan;
1245 s->range_table = &range_digital;
1246 break;
1247#endif
1248
1249 pcl816_reset(dev);
1250
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001251 printk("\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001252
1253 return 0;
1254}
1255
1256/*
1257==============================================================================
1258 Removes device
1259 */
Bill Pembertonda91b262009-04-09 16:07:03 -04001260static int pcl816_detach(struct comedi_device *dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001261{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301262 DEBUG(printk("comedi%d: pcl816: remove\n", dev->minor);)
1263 free_resources(dev);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001264#ifdef unused
1265 if (devpriv->dma_rtc)
1266 RTC_lock--;
1267#endif
1268 return 0;
1269}