blob: 9820759ec54f9da66f6c0ecb9d3cf5d0dfc32766 [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*/
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000217static int check_channel_list(struct comedi_device *dev,
218 struct comedi_subdevice *s,
219 unsigned int *chanlist, unsigned int chanlen);
220static void setup_channel_list(struct comedi_device *dev,
221 struct comedi_subdevice *s,
222 unsigned int *chanlist, unsigned int seglen);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530223static int pcl816_ai_cancel(struct comedi_device *dev,
224 struct comedi_subdevice *s);
225static void start_pacer(struct comedi_device *dev, int mode,
226 unsigned int divisor1, unsigned int divisor2);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800227#ifdef unused
228static int set_rtc_irq_bit(unsigned char bit);
229#endif
230
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530231static int pcl816_ai_cmdtest(struct comedi_device *dev,
232 struct comedi_subdevice *s,
233 struct comedi_cmd *cmd);
Bill Pembertonda91b262009-04-09 16:07:03 -0400234static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800235
236/*
237==============================================================================
238 ANALOG INPUT MODE0, 816 cards, slow version
239*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530240static int pcl816_ai_insn_read(struct comedi_device *dev,
241 struct comedi_subdevice *s,
242 struct comedi_insn *insn, unsigned int *data)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800243{
244 int n;
245 int timeout;
246
247 DPRINTK("mode 0 analog input\n");
Bill Pemberton58c05762009-03-27 11:31:22 -0400248 /* software trigger, DMA and INT off */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800249 outb(0, dev->iobase + PCL816_CONTROL);
Bill Pemberton58c05762009-03-27 11:31:22 -0400250 /* clear INT (conversion end) flag */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800251 outb(0, dev->iobase + PCL816_CLRINT);
252
Bill Pemberton58c05762009-03-27 11:31:22 -0400253 /* Set the input channel */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800254 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
255 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */
256
257 for (n = 0; n < insn->n; n++) {
258
259 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
260
261 timeout = 100;
262 while (timeout--) {
263 if (!(inb(dev->iobase + PCL816_STATUS) &
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530264 PCL816_STATUS_DRDY_MASK)) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400265 /* return read value */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800266 data[n] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530267 ((inb(dev->iobase +
268 PCL816_AD_HI) << 8) |
269 (inb(dev->iobase + PCL816_AD_LO)));
Juan Grigeradd2996b2009-02-19 09:30:57 -0800270
271 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
272 break;
273 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700274 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800275 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400276 /* Return timeout error */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800277 if (!timeout) {
278 comedi_error(dev, "A/D insn timeout\n");
279 data[0] = 0;
280 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
281 return -EIO;
282 }
283
284 }
285 return n;
286}
287
288/*
289==============================================================================
290 analog input interrupt mode 1 & 3, 818 cards
291 one sample per interrupt version
292*/
293static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
294{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400295 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400296 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800297 int low, hi;
298 int timeout = 50; /* wait max 50us */
299
300 while (timeout--) {
301 if (!(inb(dev->iobase + PCL816_STATUS) &
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530302 PCL816_STATUS_DRDY_MASK))
Juan Grigeradd2996b2009-02-19 09:30:57 -0800303 break;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700304 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800305 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400306 if (!timeout) { /* timeout, bail error */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800307 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
308 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
309 pcl816_ai_cancel(dev, s);
310 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
311 comedi_event(dev, s);
312 return IRQ_HANDLED;
313
314 }
315
Bill Pemberton58c05762009-03-27 11:31:22 -0400316 /* get the sample */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800317 low = inb(dev->iobase + PCL816_AD_LO);
318 hi = inb(dev->iobase + PCL816_AD_HI);
319
320 comedi_buf_put(s->async, (hi << 8) | low);
321
322 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
323
324 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
325 devpriv->ai_act_chanlist_pos = 0;
326
Ian Abbott13de4f02010-01-20 13:04:45 +0000327 s->async->cur_chan++;
328 if (s->async->cur_chan >= devpriv->ai_n_chan) {
329 s->async->cur_chan = 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800330 devpriv->ai_act_scan++;
331 }
332
333 if (!devpriv->ai_neverending)
334 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
335 /* all data sampled */
336 pcl816_ai_cancel(dev, s);
337 s->async->events |= COMEDI_CB_EOA;
338 }
339 comedi_event(dev, s);
340 return IRQ_HANDLED;
341}
342
343/*
344==============================================================================
345 analog input dma mode 1 & 3, 816 cards
346*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530347static void transfer_from_dma_buf(struct comedi_device *dev,
348 struct comedi_subdevice *s, short *ptr,
349 unsigned int bufptr, unsigned int len)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800350{
351 int i;
352
353 s->async->events = 0;
354
355 for (i = 0; i < len; i++) {
356
357 comedi_buf_put(s->async, ptr[bufptr++]);
358
359 if (++devpriv->ai_act_chanlist_pos >=
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530360 devpriv->ai_act_chanlist_len) {
Juan Grigeradd2996b2009-02-19 09:30:57 -0800361 devpriv->ai_act_chanlist_pos = 0;
Ian Abbott13de4f02010-01-20 13:04:45 +0000362 }
363
364 s->async->cur_chan++;
365 if (s->async->cur_chan >= devpriv->ai_n_chan) {
366 s->async->cur_chan = 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800367 devpriv->ai_act_scan++;
368 }
369
370 if (!devpriv->ai_neverending)
Bill Pemberton58c05762009-03-27 11:31:22 -0400371 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800372 pcl816_ai_cancel(dev, s);
373 s->async->events |= COMEDI_CB_EOA;
374 s->async->events |= COMEDI_CB_BLOCK;
375 break;
376 }
377 }
378
379 comedi_event(dev, s);
380}
381
382static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
383{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400384 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400385 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800386 int len, bufptr, this_dma_buf;
387 unsigned long dma_flags;
Bill Pemberton790c5542009-03-16 22:05:02 -0400388 short *ptr;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800389
390 disable_dma(devpriv->dma);
391 this_dma_buf = devpriv->next_dma_buf;
392
Bill Pemberton58c05762009-03-27 11:31:22 -0400393 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { /* switch dma bufs */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800394
395 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
396 set_dma_mode(devpriv->dma, DMA_MODE_READ);
397 dma_flags = claim_dma_lock();
Bill Pemberton58c05762009-03-27 11:31:22 -0400398/* clear_dma_ff (devpriv->dma); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800399 set_dma_addr(devpriv->dma,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530400 devpriv->hwdmaptr[devpriv->next_dma_buf]);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800401 if (devpriv->dma_runs_to_end) {
402 set_dma_count(devpriv->dma,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530403 devpriv->hwdmasize[devpriv->
404 next_dma_buf]);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800405 } else {
406 set_dma_count(devpriv->dma, devpriv->last_dma_run);
407 }
408 release_dma_lock(dma_flags);
409 enable_dma(devpriv->dma);
410 }
411
412 devpriv->dma_runs_to_end--;
413 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
414
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530415 ptr = (short *)devpriv->dmabuf[this_dma_buf];
Juan Grigeradd2996b2009-02-19 09:30:57 -0800416
417 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
418 bufptr = devpriv->ai_poll_ptr;
419 devpriv->ai_poll_ptr = 0;
420
421 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
422 return IRQ_HANDLED;
423}
424
425/*
426==============================================================================
427 INT procedure
428*/
Jiri Slaby70265d22009-03-26 09:34:06 +0100429static irqreturn_t interrupt_pcl816(int irq, void *d)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800430{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400431 struct comedi_device *dev = d;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800432 DPRINTK("<I>");
433
434 if (!dev->attached) {
435 comedi_error(dev, "premature interrupt");
436 return IRQ_HANDLED;
437 }
438
439 switch (devpriv->int816_mode) {
440 case INT_TYPE_AI1_DMA:
441 case INT_TYPE_AI3_DMA:
442 return interrupt_pcl816_ai_mode13_dma(irq, d);
443 case INT_TYPE_AI1_INT:
444 case INT_TYPE_AI3_INT:
445 return interrupt_pcl816_ai_mode13_int(irq, d);
446 }
447
448 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
449 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530450 (!devpriv->int816_mode)) {
Juan Grigeradd2996b2009-02-19 09:30:57 -0800451 if (devpriv->irq_was_now_closed) {
452 devpriv->irq_was_now_closed = 0;
Bill Pemberton58c05762009-03-27 11:31:22 -0400453 /* comedi_error(dev,"last IRQ.."); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800454 return IRQ_HANDLED;
455 }
456 comedi_error(dev, "bad IRQ!");
457 return IRQ_NONE;
458 }
André Goddard Rosabbc9a992009-11-14 13:09:06 -0200459 comedi_error(dev, "IRQ from unknown source!");
Juan Grigeradd2996b2009-02-19 09:30:57 -0800460 return IRQ_NONE;
461}
462
463/*
464==============================================================================
465 COMMAND MODE
466*/
Bill Pembertonda91b262009-04-09 16:07:03 -0400467static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800468{
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700469 printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530470 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700471 printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530472 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700473 printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530474 cmd->scan_end_src);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700475 printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530476 cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800477}
478
479/*
480==============================================================================
481*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530482static int pcl816_ai_cmdtest(struct comedi_device *dev,
483 struct comedi_subdevice *s, struct comedi_cmd *cmd)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800484{
485 int err = 0;
Ian Abbott48b1aff2009-11-20 11:32:37 +0000486 int tmp, divisor1 = 0, divisor2 = 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800487
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530488 DEBUG(printk("pcl816 pcl812_ai_cmdtest\n"); pcl816_cmdtest_out(-1, cmd);
489 );
Juan Grigeradd2996b2009-02-19 09:30:57 -0800490
491 /* step 1: make sure trigger sources are trivially valid */
492 tmp = cmd->start_src;
493 cmd->start_src &= TRIG_NOW;
494 if (!cmd->start_src || tmp != cmd->start_src)
495 err++;
496
497 tmp = cmd->scan_begin_src;
498 cmd->scan_begin_src &= TRIG_FOLLOW;
499 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
500 err++;
501
Ian Abbottefdf83c2009-11-20 11:32:38 +0000502 tmp = cmd->convert_src;
503 cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
504 if (!cmd->convert_src || tmp != cmd->convert_src)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800505 err++;
506
507 tmp = cmd->scan_end_src;
508 cmd->scan_end_src &= TRIG_COUNT;
509 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
510 err++;
511
512 tmp = cmd->stop_src;
513 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
514 if (!cmd->stop_src || tmp != cmd->stop_src)
515 err++;
516
517 if (err) {
518 return 1;
519 }
520
521 /* step 2: make sure trigger sources are unique and mutually compatible */
522
523 if (cmd->start_src != TRIG_NOW) {
524 cmd->start_src = TRIG_NOW;
525 err++;
526 }
527
528 if (cmd->scan_begin_src != TRIG_FOLLOW) {
529 cmd->scan_begin_src = TRIG_FOLLOW;
530 err++;
531 }
532
533 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
534 cmd->convert_src = TRIG_TIMER;
535 err++;
536 }
537
538 if (cmd->scan_end_src != TRIG_COUNT) {
539 cmd->scan_end_src = TRIG_COUNT;
540 err++;
541 }
542
543 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
544 err++;
545
546 if (err) {
547 return 2;
548 }
549
550 /* step 3: make sure arguments are trivially compatible */
551 if (cmd->start_arg != 0) {
552 cmd->start_arg = 0;
553 err++;
554 }
555
556 if (cmd->scan_begin_arg != 0) {
557 cmd->scan_begin_arg = 0;
558 err++;
559 }
560 if (cmd->convert_src == TRIG_TIMER) {
561 if (cmd->convert_arg < this_board->ai_ns_min) {
562 cmd->convert_arg = this_board->ai_ns_min;
563 err++;
564 }
565 } else { /* TRIG_EXT */
566 if (cmd->convert_arg != 0) {
567 cmd->convert_arg = 0;
568 err++;
569 }
570 }
571
Juan Grigeradd2996b2009-02-19 09:30:57 -0800572 if (cmd->scan_end_arg != cmd->chanlist_len) {
573 cmd->scan_end_arg = cmd->chanlist_len;
574 err++;
575 }
576 if (cmd->stop_src == TRIG_COUNT) {
577 if (!cmd->stop_arg) {
578 cmd->stop_arg = 1;
579 err++;
580 }
581 } else { /* TRIG_NONE */
582 if (cmd->stop_arg != 0) {
583 cmd->stop_arg = 0;
584 err++;
585 }
586 }
587
588 if (err) {
589 return 3;
590 }
591
592 /* step 4: fix up any arguments */
593 if (cmd->convert_src == TRIG_TIMER) {
594 tmp = cmd->convert_arg;
595 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530596 &divisor1, &divisor2,
597 &cmd->convert_arg,
598 cmd->flags & TRIG_ROUND_MASK);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800599 if (cmd->convert_arg < this_board->ai_ns_min)
600 cmd->convert_arg = this_board->ai_ns_min;
601 if (tmp != cmd->convert_arg)
602 err++;
603 }
604
605 if (err) {
606 return 4;
607 }
608
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000609 /* step 5: complain about special chanlist considerations */
610
611 if (cmd->chanlist) {
612 if (!check_channel_list(dev, s, cmd->chanlist,
613 cmd->chanlist_len))
614 return 5; /* incorrect channels list */
615 }
616
Juan Grigeradd2996b2009-02-19 09:30:57 -0800617 return 0;
618}
619
Bill Pembertonda91b262009-04-09 16:07:03 -0400620static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800621{
622 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400623 struct comedi_cmd *cmd = &s->async->cmd;
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000624 unsigned int seglen;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800625
626 if (cmd->start_src != TRIG_NOW)
627 return -EINVAL;
628 if (cmd->scan_begin_src != TRIG_FOLLOW)
629 return -EINVAL;
630 if (cmd->scan_end_src != TRIG_COUNT)
631 return -EINVAL;
632 if (cmd->scan_end_arg != cmd->chanlist_len)
633 return -EINVAL;
Bill Pemberton58c05762009-03-27 11:31:22 -0400634/* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800635 if (devpriv->irq_blocked)
636 return -EBUSY;
637
638 if (cmd->convert_src == TRIG_TIMER) {
639 if (cmd->convert_arg < this_board->ai_ns_min)
640 cmd->convert_arg = this_board->ai_ns_min;
641
642 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530643 &divisor2, &cmd->convert_arg,
644 cmd->flags & TRIG_ROUND_MASK);
Bill Pemberton58c05762009-03-27 11:31:22 -0400645 if (divisor1 == 1) { /* PCL816 crash if any divisor is set to 1 */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800646 divisor1 = 2;
647 divisor2 /= 2;
648 }
649 if (divisor2 == 1) {
650 divisor2 = 2;
651 divisor1 /= 2;
652 }
653 }
654
Bill Pemberton58c05762009-03-27 11:31:22 -0400655 start_pacer(dev, -1, 0, 0); /* stop pacer */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800656
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000657 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
658 if (seglen < 1)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800659 return -EINVAL;
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000660 setup_channel_list(dev, s, cmd->chanlist, seglen);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700661 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800662
Ian Abbott13de4f02010-01-20 13:04:45 +0000663 devpriv->ai_n_chan = cmd->chanlist_len;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800664 devpriv->ai_act_scan = 0;
665 s->async->cur_chan = 0;
666 devpriv->irq_blocked = 1;
667 devpriv->ai_poll_ptr = 0;
668 devpriv->irq_was_now_closed = 0;
669
670 if (cmd->stop_src == TRIG_COUNT) {
671 devpriv->ai_scans = cmd->stop_arg;
672 devpriv->ai_neverending = 0;
673 } else {
674 devpriv->ai_scans = 0;
675 devpriv->ai_neverending = 1;
676 }
677
Bill Pemberton58c05762009-03-27 11:31:22 -0400678 if ((cmd->flags & TRIG_WAKE_EOS)) { /* don't we want wake up every scan? */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800679 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
Bill Pemberton58c05762009-03-27 11:31:22 -0400680 /* devpriv->ai_eos=1; */
681 /* if (devpriv->ai_n_chan==1) */
682 /* devpriv->dma=0; // DMA is useless for this situation */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800683 }
684
685 if (devpriv->dma) {
686 bytes = devpriv->hwdmasize[0];
687 if (!devpriv->ai_neverending) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400688 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); /* how many */
689 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fill */
690 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800691 devpriv->dma_runs_to_end--;
692 if (devpriv->dma_runs_to_end >= 0)
693 bytes = devpriv->hwdmasize[0];
694 } else
695 devpriv->dma_runs_to_end = -1;
696
697 devpriv->next_dma_buf = 0;
698 set_dma_mode(devpriv->dma, DMA_MODE_READ);
699 dma_flags = claim_dma_lock();
700 clear_dma_ff(devpriv->dma);
701 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
702 set_dma_count(devpriv->dma, bytes);
703 release_dma_lock(dma_flags);
704 enable_dma(devpriv->dma);
705 }
706
707 start_pacer(dev, 1, divisor1, divisor2);
708 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
709
710 switch (cmd->convert_src) {
711 case TRIG_TIMER:
712 devpriv->int816_mode = INT_TYPE_AI1_DMA;
Bill Pemberton58c05762009-03-27 11:31:22 -0400713 outb(0x32, dev->iobase + PCL816_CONTROL); /* Pacer+IRQ+DMA */
714 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq and DMA to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800715 break;
716
717 default:
718 devpriv->int816_mode = INT_TYPE_AI3_DMA;
Bill Pemberton58c05762009-03-27 11:31:22 -0400719 outb(0x34, dev->iobase + PCL816_CONTROL); /* Ext trig+IRQ+DMA */
720 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800721 break;
722 }
723
724 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
725 return 0;
726}
727
Bill Pembertonda91b262009-04-09 16:07:03 -0400728static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800729{
730 unsigned long flags;
731 unsigned int top1, top2, i;
732
733 if (!devpriv->dma)
Bill Pemberton58c05762009-03-27 11:31:22 -0400734 return 0; /* poll is valid only for DMA transfer */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800735
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700736 spin_lock_irqsave(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800737
738 for (i = 0; i < 20; i++) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400739 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800740 top2 = get_dma_residue(devpriv->dma);
741 if (top1 == top2)
742 break;
743 }
744 if (top1 != top2) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700745 spin_unlock_irqrestore(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800746 return 0;
747 }
748
Bill Pemberton58c05762009-03-27 11:31:22 -0400749 top1 = devpriv->hwdmasize[0] - top1; /* where is now DMA in buffer */
750 top1 >>= 1; /* sample position */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800751 top2 = top1 - devpriv->ai_poll_ptr;
Bill Pemberton58c05762009-03-27 11:31:22 -0400752 if (top2 < 1) { /* no new samples */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700753 spin_unlock_irqrestore(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800754 return 0;
755 }
756
757 transfer_from_dma_buf(dev, s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530758 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
759 devpriv->ai_poll_ptr, top2);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800760
Bill Pemberton58c05762009-03-27 11:31:22 -0400761 devpriv->ai_poll_ptr = top1; /* new buffer position */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700762 spin_unlock_irqrestore(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800763
764 return s->async->buf_write_count - s->async->buf_read_count;
765}
766
767/*
768==============================================================================
769 cancel any mode 1-4 AI
770*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530771static int pcl816_ai_cancel(struct comedi_device *dev,
772 struct comedi_subdevice *s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800773{
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700774/* DEBUG(printk("pcl816_ai_cancel()\n");) */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800775
776 if (devpriv->irq_blocked > 0) {
777 switch (devpriv->int816_mode) {
778#ifdef unused
779 case INT_TYPE_AI1_DMA_RTC:
780 case INT_TYPE_AI3_DMA_RTC:
Bill Pemberton58c05762009-03-27 11:31:22 -0400781 set_rtc_irq_bit(0); /* stop RTC */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800782 del_timer(&devpriv->rtc_irq_timer);
783#endif
784 case INT_TYPE_AI1_DMA:
785 case INT_TYPE_AI3_DMA:
786 disable_dma(devpriv->dma);
787 case INT_TYPE_AI1_INT:
788 case INT_TYPE_AI3_INT:
789 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700790 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800791 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
792 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
793 outb(0x70, dev->iobase + PCL816_CTRCTL);
794 outb(0, dev->iobase + PCL816_AD_LO);
795 inb(dev->iobase + PCL816_AD_LO);
796 inb(dev->iobase + PCL816_AD_HI);
797 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
798 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
799 devpriv->irq_blocked = 0;
800 devpriv->irq_was_now_closed = devpriv->int816_mode;
801 devpriv->int816_mode = 0;
802 devpriv->last_int_sub = s;
Bill Pemberton58c05762009-03-27 11:31:22 -0400803/* s->busy = 0; */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800804 break;
805 }
806 }
807
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530808 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
809 return 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800810}
811
812/*
813==============================================================================
814 chech for PCL816
815*/
816static int pcl816_check(unsigned long iobase)
817{
818 outb(0x00, iobase + PCL816_MUX);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700819 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800820 if (inb(iobase + PCL816_MUX) != 0x00)
Bill Pemberton58c05762009-03-27 11:31:22 -0400821 return 1; /* there isn't card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800822 outb(0x55, iobase + PCL816_MUX);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700823 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800824 if (inb(iobase + PCL816_MUX) != 0x55)
Bill Pemberton58c05762009-03-27 11:31:22 -0400825 return 1; /* there isn't card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800826 outb(0x00, iobase + PCL816_MUX);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700827 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800828 outb(0x18, iobase + PCL816_CONTROL);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700829 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800830 if (inb(iobase + PCL816_CONTROL) != 0x18)
Bill Pemberton58c05762009-03-27 11:31:22 -0400831 return 1; /* there isn't card */
832 return 0; /* ok, card exist */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800833}
834
835/*
836==============================================================================
837 reset whole PCL-816 cards
838*/
Bill Pembertonda91b262009-04-09 16:07:03 -0400839static void pcl816_reset(struct comedi_device *dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800840{
Bill Pemberton58c05762009-03-27 11:31:22 -0400841/* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
842/* outb (0, dev->iobase + PCL818_DA_HI); */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700843/* udelay (1); */
Bill Pemberton58c05762009-03-27 11:31:22 -0400844/* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
845/* outb (0, dev->iobase + PCL818_DO_LO); */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700846/* udelay (1); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800847 outb(0, dev->iobase + PCL816_CONTROL);
848 outb(0, dev->iobase + PCL816_MUX);
849 outb(0, dev->iobase + PCL816_CLRINT);
850 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
851 outb(0x70, dev->iobase + PCL816_CTRCTL);
852 outb(0x30, dev->iobase + PCL816_CTRCTL);
853 outb(0, dev->iobase + PCL816_RANGE);
854}
855
856/*
857==============================================================================
858 Start/stop pacer onboard pacer
859*/
860static void
Bill Pembertonda91b262009-04-09 16:07:03 -0400861start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530862 unsigned int divisor2)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800863{
864 outb(0x32, dev->iobase + PCL816_CTRCTL);
865 outb(0xff, dev->iobase + PCL816_CTR0);
866 outb(0x00, dev->iobase + PCL816_CTR0);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700867 udelay(1);
Bill Pemberton58c05762009-03-27 11:31:22 -0400868 outb(0xb4, dev->iobase + PCL816_CTRCTL); /* set counter 2 as mode 3 */
869 outb(0x74, dev->iobase + PCL816_CTRCTL); /* set counter 1 as mode 3 */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700870 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800871
872 if (mode == 1) {
873 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
874 divisor2);
875 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
876 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
877 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
878 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
879 }
880
881 /* clear pending interrupts (just in case) */
Bill Pemberton58c05762009-03-27 11:31:22 -0400882/* outb(0, dev->iobase + PCL816_CLRINT); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800883}
884
885/*
886==============================================================================
887 Check if channel list from user is builded correctly
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000888 If it's ok, then return non-zero length of repeated segment of channel list
Juan Grigeradd2996b2009-02-19 09:30:57 -0800889*/
890static int
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000891check_channel_list(struct comedi_device *dev,
892 struct comedi_subdevice *s, unsigned int *chanlist,
893 unsigned int chanlen)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800894{
895 unsigned int chansegment[16];
896 unsigned int i, nowmustbechan, seglen, segpos;
897
Bill Pemberton58c05762009-03-27 11:31:22 -0400898 /* correct channel and range number check itself comedi/range.c */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800899 if (chanlen < 1) {
900 comedi_error(dev, "range/channel list is empty!");
901 return 0;
902 }
903
904 if (chanlen > 1) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400905 chansegment[0] = chanlist[0]; /* first channel is everytime ok */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800906 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400907 /* build part of chanlist */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700908 DEBUG(printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530909 CR_RANGE(chanlist[i]));)
910 if (chanlist[0] == chanlist[i])
Bill Pemberton58c05762009-03-27 11:31:22 -0400911 break; /* we detect loop, this must by finish */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800912 nowmustbechan =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530913 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800914 if (nowmustbechan != CR_CHAN(chanlist[i])) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400915 /* channel list isn't continous :-( */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700916 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530917 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
918 dev->minor, i, CR_CHAN(chanlist[i]),
919 nowmustbechan, CR_CHAN(chanlist[0]));
Juan Grigeradd2996b2009-02-19 09:30:57 -0800920 return 0;
921 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400922 chansegment[i] = chanlist[i]; /* well, this is next correct channel in list */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800923 }
924
Bill Pemberton58c05762009-03-27 11:31:22 -0400925 for (i = 0, segpos = 0; i < chanlen; i++) { /* check whole chanlist */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700926 DEBUG(printk("%d %d=%d %d\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530927 CR_CHAN(chansegment[i % seglen]),
928 CR_RANGE(chansegment[i % seglen]),
929 CR_CHAN(chanlist[i]),
930 CR_RANGE(chanlist[i]));)
931 if (chanlist[i] != chansegment[i % seglen]) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700932 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530933 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
934 dev->minor, i, CR_CHAN(chansegment[i]),
935 CR_RANGE(chansegment[i]),
936 CR_AREF(chansegment[i]),
937 CR_CHAN(chanlist[i % seglen]),
938 CR_RANGE(chanlist[i % seglen]),
939 CR_AREF(chansegment[i % seglen]));
Bill Pemberton58c05762009-03-27 11:31:22 -0400940 return 0; /* chan/gain list is strange */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800941 }
942 }
943 } else {
944 seglen = 1;
945 }
946
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000947 return seglen; /* we can serve this with MUX logic */
948}
949
950/*
951==============================================================================
952 Program scan/gain logic with channel list.
953*/
954static void
955setup_channel_list(struct comedi_device *dev,
956 struct comedi_subdevice *s, unsigned int *chanlist,
957 unsigned int seglen)
958{
959 unsigned int i;
960
Juan Grigeradd2996b2009-02-19 09:30:57 -0800961 devpriv->ai_act_chanlist_len = seglen;
962 devpriv->ai_act_chanlist_pos = 0;
963
Bill Pemberton58c05762009-03-27 11:31:22 -0400964 for (i = 0; i < seglen; i++) { /* store range list to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800965 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
966 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
967 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */
968 }
969
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700970 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800971
972 outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800973}
974
975#ifdef unused
976/*
977==============================================================================
978 Enable(1)/disable(0) periodic interrupts from RTC
979*/
980static int set_rtc_irq_bit(unsigned char bit)
981{
982 unsigned char val;
983 unsigned long flags;
984
985 if (bit == 1) {
986 RTC_timer_lock++;
987 if (RTC_timer_lock > 1)
988 return 0;
989 } else {
990 RTC_timer_lock--;
991 if (RTC_timer_lock < 0)
992 RTC_timer_lock = 0;
993 if (RTC_timer_lock > 0)
994 return 0;
995 }
996
997 save_flags(flags);
998 cli();
999 val = CMOS_READ(RTC_CONTROL);
1000 if (bit) {
1001 val |= RTC_PIE;
1002 } else {
1003 val &= ~RTC_PIE;
1004 }
1005 CMOS_WRITE(val, RTC_CONTROL);
1006 CMOS_READ(RTC_INTR_FLAGS);
1007 restore_flags(flags);
1008 return 0;
1009}
1010#endif
1011
1012/*
1013==============================================================================
1014 Free any resources that we have claimed
1015*/
Bill Pembertonda91b262009-04-09 16:07:03 -04001016static void free_resources(struct comedi_device *dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001017{
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001018 /* printk("free_resource()\n"); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001019 if (dev->private) {
1020 pcl816_ai_cancel(dev, devpriv->sub_ai);
1021 pcl816_reset(dev);
1022 if (devpriv->dma)
1023 free_dma(devpriv->dma);
1024 if (devpriv->dmabuf[0])
1025 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1026 if (devpriv->dmabuf[1])
1027 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1028#ifdef unused
1029 if (devpriv->rtc_irq)
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001030 free_irq(devpriv->rtc_irq, dev);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001031 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1032 if (devpriv->rtc_iobase)
1033 release_region(devpriv->rtc_iobase,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301034 devpriv->rtc_iosize);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001035 }
1036#endif
1037 }
1038
1039 if (dev->irq)
1040 free_irq(dev->irq, dev);
1041 if (dev->iobase)
1042 release_region(dev->iobase, this_board->io_range);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001043 /* printk("free_resource() end\n"); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001044}
1045
1046/*
1047==============================================================================
1048
1049 Initialization
1050
1051*/
Bill Pembertonda91b262009-04-09 16:07:03 -04001052static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001053{
1054 int ret;
1055 unsigned long iobase;
1056 unsigned int irq, dma;
1057 unsigned long pages;
Bill Pemberton58c05762009-03-27 11:31:22 -04001058 /* int i; */
Bill Pemberton34c43922009-03-16 22:05:14 -04001059 struct comedi_subdevice *s;
Juan Grigeradd2996b2009-02-19 09:30:57 -08001060
1061 /* claim our I/O space */
1062 iobase = it->options[0];
1063 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301064 this_board->name, iobase);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001065
1066 if (!request_region(iobase, this_board->io_range, "pcl816")) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001067 printk("I/O port conflict\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001068 return -EIO;
1069 }
1070
1071 dev->iobase = iobase;
1072
1073 if (pcl816_check(iobase)) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001074 printk(", I cann't detect board. FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001075 return -EIO;
1076 }
1077
Bill Pembertonc3744132009-04-22 21:11:47 -04001078 ret = alloc_private(dev, sizeof(struct pcl816_private));
1079 if (ret < 0)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001080 return ret; /* Can't alloc mem */
1081
1082 /* set up some name stuff */
1083 dev->board_name = this_board->name;
1084
1085 /* grab our IRQ */
1086 irq = 0;
1087 if (this_board->IRQbits != 0) { /* board support IRQ */
1088 irq = it->options[1];
1089 if (irq) { /* we want to use IRQ */
1090 if (((1 << irq) & this_board->IRQbits) == 0) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001091 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301092 (", IRQ %u is out of allowed range, DISABLING IT",
1093 irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001094 irq = 0; /* Bad IRQ */
1095 } else {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301096 if (request_irq
1097 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001098 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301099 (", unable to allocate IRQ %u, DISABLING IT",
1100 irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001101 irq = 0; /* Can't use IRQ */
1102 } else {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001103 printk(", irq=%u", irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001104 }
1105 }
1106 }
1107 }
1108
1109 dev->irq = irq;
1110 if (irq) {
1111 devpriv->irq_free = 1;
1112 } /* 1=we have allocated irq */
1113 else {
1114 devpriv->irq_free = 0;
1115 }
1116 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1117 devpriv->int816_mode = 0; /* mode of irq */
1118
1119#ifdef unused
1120 /* grab RTC for DMA operations */
1121 devpriv->dma_rtc = 0;
Bill Pemberton58c05762009-03-27 11:31:22 -04001122 if (it->options[2] > 0) { /* we want to use DMA */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001123 if (RTC_lock == 0) {
1124 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301125 "pcl816 (RTC)"))
Juan Grigeradd2996b2009-02-19 09:30:57 -08001126 goto no_rtc;
1127 }
1128 devpriv->rtc_iobase = RTC_PORT(0);
1129 devpriv->rtc_iosize = RTC_IO_EXTENT;
1130 RTC_lock++;
1131#ifdef UNTESTED_CODE
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001132 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301133 "pcl816 DMA (RTC)", dev)) {
Juan Grigeradd2996b2009-02-19 09:30:57 -08001134 devpriv->dma_rtc = 1;
1135 devpriv->rtc_irq = RTC_IRQ;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001136 printk(", dma_irq=%u", devpriv->rtc_irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001137 } else {
1138 RTC_lock--;
1139 if (RTC_lock == 0) {
1140 if (devpriv->rtc_iobase)
1141 release_region(devpriv->rtc_iobase,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301142 devpriv->rtc_iosize);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001143 }
1144 devpriv->rtc_iobase = 0;
1145 devpriv->rtc_iosize = 0;
1146 }
1147#else
1148 printk("pcl816: RTC code missing");
1149#endif
1150
1151 }
1152
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301153no_rtc:
Juan Grigeradd2996b2009-02-19 09:30:57 -08001154#endif
1155 /* grab our DMA */
1156 dma = 0;
1157 devpriv->dma = dma;
1158 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1159 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1160
1161 if (this_board->DMAbits != 0) { /* board support DMA */
1162 dma = it->options[2];
1163 if (dma < 1)
1164 goto no_dma; /* DMA disabled */
1165
1166 if (((1 << dma) & this_board->DMAbits) == 0) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001167 printk(", DMA is out of allowed range, FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001168 return -EINVAL; /* Bad DMA */
1169 }
1170 ret = request_dma(dma, "pcl816");
1171 if (ret) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001172 printk(", unable to allocate DMA %u, FAIL!\n", dma);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001173 return -EBUSY; /* DMA isn't free */
1174 }
1175
1176 devpriv->dma = dma;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001177 printk(", dma=%u", dma);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001178 pages = 2; /* we need 16KB */
1179 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1180
1181 if (!devpriv->dmabuf[0]) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001182 printk(", unable to allocate DMA buffer, FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001183 /* maybe experiment with try_to_free_pages() will help .... */
1184 return -EBUSY; /* no buffer :-( */
1185 }
1186 devpriv->dmapages[0] = pages;
1187 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1188 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001189 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001190
Bill Pemberton58c05762009-03-27 11:31:22 -04001191 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001192 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1193 if (!devpriv->dmabuf[1]) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001194 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301195 (", unable to allocate DMA buffer, FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001196 return -EBUSY;
1197 }
1198 devpriv->dmapages[1] = pages;
1199 devpriv->hwdmaptr[1] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301200 virt_to_bus((void *)devpriv->dmabuf[1]);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001201 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1202 }
1203 }
1204
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301205no_dma:
Juan Grigeradd2996b2009-02-19 09:30:57 -08001206
1207/* if (this_board->n_aochan > 0)
1208 subdevs[1] = COMEDI_SUBD_AO;
1209 if (this_board->n_dichan > 0)
1210 subdevs[2] = COMEDI_SUBD_DI;
1211 if (this_board->n_dochan > 0)
1212 subdevs[3] = COMEDI_SUBD_DO;
1213*/
Bill Pembertonc3744132009-04-22 21:11:47 -04001214
1215 ret = alloc_subdevices(dev, 1);
1216 if (ret < 0)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001217 return ret;
1218
1219 s = dev->subdevices + 0;
1220 if (this_board->n_aichan > 0) {
1221 s->type = COMEDI_SUBD_AI;
1222 devpriv->sub_ai = s;
1223 dev->read_subdev = s;
1224 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1225 s->n_chan = this_board->n_aichan;
1226 s->subdev_flags |= SDF_DIFF;
Bill Pemberton58c05762009-03-27 11:31:22 -04001227 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001228 s->maxdata = this_board->ai_maxdata;
1229 s->len_chanlist = this_board->ai_chanlist;
1230 s->range_table = this_board->ai_range_type;
1231 s->cancel = pcl816_ai_cancel;
1232 s->do_cmdtest = pcl816_ai_cmdtest;
1233 s->do_cmd = pcl816_ai_cmd;
1234 s->poll = pcl816_ai_poll;
1235 s->insn_read = pcl816_ai_insn_read;
1236 } else {
1237 s->type = COMEDI_SUBD_UNUSED;
1238 }
1239
1240#if 0
1241case COMEDI_SUBD_AO:
1242 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1243 s->n_chan = this_board->n_aochan;
1244 s->maxdata = this_board->ao_maxdata;
1245 s->len_chanlist = this_board->ao_chanlist;
1246 s->range_table = this_board->ao_range_type;
1247 break;
1248
1249case COMEDI_SUBD_DI:
1250 s->subdev_flags = SDF_READABLE;
1251 s->n_chan = this_board->n_dichan;
1252 s->maxdata = 1;
1253 s->len_chanlist = this_board->n_dichan;
1254 s->range_table = &range_digital;
1255 break;
1256
1257case COMEDI_SUBD_DO:
1258 s->subdev_flags = SDF_WRITABLE;
1259 s->n_chan = this_board->n_dochan;
1260 s->maxdata = 1;
1261 s->len_chanlist = this_board->n_dochan;
1262 s->range_table = &range_digital;
1263 break;
1264#endif
1265
1266 pcl816_reset(dev);
1267
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001268 printk("\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001269
1270 return 0;
1271}
1272
1273/*
1274==============================================================================
1275 Removes device
1276 */
Bill Pembertonda91b262009-04-09 16:07:03 -04001277static int pcl816_detach(struct comedi_device *dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001278{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301279 DEBUG(printk("comedi%d: pcl816: remove\n", dev->minor);)
1280 free_resources(dev);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001281#ifdef unused
1282 if (devpriv->dma_rtc)
1283 RTC_lock--;
1284#endif
1285 return 0;
1286}