blob: 71c2a3aa379e419e477b3973e90db17b95b8f501 [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090039#include <linux/gfp.h>
Juan Grigeradd2996b2009-02-19 09:30:57 -080040#include <linux/delay.h>
41#include <asm/dma.h>
42
43#include "8253.h"
44
45#define DEBUG(x) x
46
Bill Pemberton58c05762009-03-27 11:31:22 -040047/* boards constants */
48/* IO space len */
Juan Grigeradd2996b2009-02-19 09:30:57 -080049#define PCLx1x_RANGE 16
50
Bill Pemberton58c05762009-03-27 11:31:22 -040051/* #define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
Juan Grigeradd2996b2009-02-19 09:30:57 -080052
Bill Pemberton58c05762009-03-27 11:31:22 -040053/* INTEL 8254 counters */
Juan Grigeradd2996b2009-02-19 09:30:57 -080054#define PCL816_CTR0 4
55#define PCL816_CTR1 5
56#define PCL816_CTR2 6
Bill Pemberton58c05762009-03-27 11:31:22 -040057/* R: counter read-back register W: counter control */
Juan Grigeradd2996b2009-02-19 09:30:57 -080058#define PCL816_CTRCTL 7
59
Bill Pemberton58c05762009-03-27 11:31:22 -040060/* R: A/D high byte W: A/D range control */
Juan Grigeradd2996b2009-02-19 09:30:57 -080061#define PCL816_RANGE 9
Bill Pemberton58c05762009-03-27 11:31:22 -040062/* W: clear INT request */
Juan Grigeradd2996b2009-02-19 09:30:57 -080063#define PCL816_CLRINT 10
Bill Pemberton58c05762009-03-27 11:31:22 -040064/* R: next mux scan channel W: mux scan channel & range control pointer */
Juan Grigeradd2996b2009-02-19 09:30:57 -080065#define PCL816_MUX 11
Bill Pemberton58c05762009-03-27 11:31:22 -040066/* R/W: operation control register */
Juan Grigeradd2996b2009-02-19 09:30:57 -080067#define PCL816_CONTROL 12
68
Bill Pemberton58c05762009-03-27 11:31:22 -040069/* R: return status byte W: set DMA/IRQ */
Juan Grigeradd2996b2009-02-19 09:30:57 -080070#define PCL816_STATUS 13
71#define PCL816_STATUS_DRDY_MASK 0x80
72
Bill Pemberton58c05762009-03-27 11:31:22 -040073/* R: low byte of A/D W: soft A/D trigger */
Juan Grigeradd2996b2009-02-19 09:30:57 -080074#define PCL816_AD_LO 8
Bill Pemberton58c05762009-03-27 11:31:22 -040075/* R: high byte of A/D W: A/D range control */
Juan Grigeradd2996b2009-02-19 09:30:57 -080076#define PCL816_AD_HI 9
77
Bill Pemberton58c05762009-03-27 11:31:22 -040078/* type of interrupt handler */
Juan Grigeradd2996b2009-02-19 09:30:57 -080079#define INT_TYPE_AI1_INT 1
80#define INT_TYPE_AI1_DMA 2
81#define INT_TYPE_AI3_INT 4
82#define INT_TYPE_AI3_DMA 5
83#ifdef unused
84#define INT_TYPE_AI1_DMA_RTC 9
85#define INT_TYPE_AI3_DMA_RTC 10
86
Bill Pemberton58c05762009-03-27 11:31:22 -040087/* RTC stuff... */
Juan Grigeradd2996b2009-02-19 09:30:57 -080088#define RTC_IRQ 8
89#define RTC_IO_EXTENT 0x10
90#endif
91
92#define MAGIC_DMA_WORD 0x5a5a
93
Bill Pemberton9ced1de2009-03-16 22:05:31 -040094static const struct comedi_lrange range_pcl816 = { 8, {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +053095 BIP_RANGE(10),
96 BIP_RANGE(5),
97 BIP_RANGE(2.5),
98 BIP_RANGE(1.25),
99 UNI_RANGE(10),
100 UNI_RANGE(5),
101 UNI_RANGE(2.5),
102 UNI_RANGE(1.25),
103 }
Juan Grigeradd2996b2009-02-19 09:30:57 -0800104};
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530105
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400106struct pcl816_board {
107
Bill Pemberton58c05762009-03-27 11:31:22 -0400108 const char *name; /* board name */
109 int n_ranges; /* len of range list */
110 int n_aichan; /* num of A/D chans in diferencial mode */
111 unsigned int ai_ns_min; /* minimal alllowed delay between samples (in ns) */
112 int n_aochan; /* num of D/A chans */
113 int n_dichan; /* num of DI chans */
114 int n_dochan; /* num of DO chans */
115 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
André Goddard Rosabbc9a992009-11-14 13:09:06 -0200116 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
Bill Pemberton58c05762009-03-27 11:31:22 -0400117 unsigned int io_range; /* len of IO space */
118 unsigned int IRQbits; /* allowed interrupts */
119 unsigned int DMAbits; /* allowed DMA chans */
120 int ai_maxdata; /* maxdata for A/D */
121 int ao_maxdata; /* maxdata for D/A */
122 int ai_chanlist; /* allowed len of channel list A/D */
123 int ao_chanlist; /* allowed len of channel list D/A */
124 int i8254_osc_base; /* 1/frequency of on board oscilator in ns */
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400125};
Juan Grigeradd2996b2009-02-19 09:30:57 -0800126
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400127static const struct pcl816_board boardtypes[] = {
Juan Grigeradd2996b2009-02-19 09:30:57 -0800128 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530129 &range_pcl816, PCLx1x_RANGE,
130 0x00fc, /* IRQ mask */
131 0x0a, /* DMA mask */
132 0xffff, /* 16-bit card */
133 0xffff, /* D/A maxdata */
134 1024,
135 1, /* ao chan list */
136 100},
Juan Grigeradd2996b2009-02-19 09:30:57 -0800137 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530138 &range_pcl816, PCLx1x_RANGE,
139 0x00fc,
140 0x0a,
141 0x3fff, /* 14 bit card */
142 0x3fff,
143 1024,
144 1,
145 100},
Juan Grigeradd2996b2009-02-19 09:30:57 -0800146};
147
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400148#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board))
Bill Pembertonfe0ff172009-03-16 22:18:43 -0400149#define devpriv ((struct pcl816_private *)dev->private)
Bill Pemberton1c7f40d2009-03-16 22:17:11 -0400150#define this_board ((const struct pcl816_board *)dev->board_ptr)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800151
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530152static int pcl816_attach(struct comedi_device *dev,
153 struct comedi_devconfig *it);
Bill Pembertonda91b262009-04-09 16:07:03 -0400154static int pcl816_detach(struct comedi_device *dev);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800155
156#ifdef unused
157static int RTC_lock = 0; /* RTC lock */
158static int RTC_timer_lock = 0; /* RTC int lock */
159#endif
160
Bill Pemberton139dfbd2009-03-16 22:05:25 -0400161static struct comedi_driver driver_pcl816 = {
Bill Pemberton68c3dbf2009-04-22 21:11:49 -0400162 .driver_name = "pcl816",
163 .module = THIS_MODULE,
164 .attach = pcl816_attach,
165 .detach = pcl816_detach,
166 .board_name = &boardtypes[0].name,
167 .num_names = n_boardtypes,
168 .offset = sizeof(struct pcl816_board),
Juan Grigeradd2996b2009-02-19 09:30:57 -0800169};
170
171COMEDI_INITCLEANUP(driver_pcl816);
172
Bill Pembertonfe0ff172009-03-16 22:18:43 -0400173struct pcl816_private {
174
Bill Pemberton58c05762009-03-27 11:31:22 -0400175 unsigned int dma; /* used DMA, 0=don't use DMA */
176 int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800177#ifdef unused
Bill Pemberton58c05762009-03-27 11:31:22 -0400178 unsigned long rtc_iobase; /* RTC port region */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800179 unsigned int rtc_iosize;
180 unsigned int rtc_irq;
181#endif
Bill Pemberton58c05762009-03-27 11:31:22 -0400182 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
183 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
184 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
185 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
186 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
187 unsigned int last_top_dma; /* DMA pointer in last RTC int */
188 int next_dma_buf; /* which DMA buffer will be used next round */
189 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
190 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800191
Bill Pemberton58c05762009-03-27 11:31:22 -0400192 unsigned int ai_scans; /* len of scanlist */
193 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
194 int irq_free; /* 1=have allocated IRQ */
195 int irq_blocked; /* 1=IRQ now uses any subdev */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800196#ifdef unused
Bill Pemberton58c05762009-03-27 11:31:22 -0400197 int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800198#endif
Bill Pemberton58c05762009-03-27 11:31:22 -0400199 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
200 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
201 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
202 int ai_act_scan; /* how many scans we finished */
203 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
204 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
205 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
Ian Abbott13de4f02010-01-20 13:04:45 +0000206 unsigned int ai_n_chan; /* how many channels per scan */
Bill Pemberton58c05762009-03-27 11:31:22 -0400207 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
208 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800209#ifdef unused
Bill Pemberton58c05762009-03-27 11:31:22 -0400210 struct timer_list rtc_irq_timer; /* timer for RTC sanity check */
211 unsigned long rtc_freq; /* RTC int freq */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800212#endif
Bill Pembertonfe0ff172009-03-16 22:18:43 -0400213};
214
Juan Grigeradd2996b2009-02-19 09:30:57 -0800215/*
216==============================================================================
217*/
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000218static int check_channel_list(struct comedi_device *dev,
219 struct comedi_subdevice *s,
220 unsigned int *chanlist, unsigned int chanlen);
221static void setup_channel_list(struct comedi_device *dev,
222 struct comedi_subdevice *s,
223 unsigned int *chanlist, unsigned int seglen);
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530224static int pcl816_ai_cancel(struct comedi_device *dev,
225 struct comedi_subdevice *s);
226static void start_pacer(struct comedi_device *dev, int mode,
227 unsigned int divisor1, unsigned int divisor2);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800228#ifdef unused
229static int set_rtc_irq_bit(unsigned char bit);
230#endif
231
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530232static int pcl816_ai_cmdtest(struct comedi_device *dev,
233 struct comedi_subdevice *s,
234 struct comedi_cmd *cmd);
Bill Pembertonda91b262009-04-09 16:07:03 -0400235static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800236
237/*
238==============================================================================
239 ANALOG INPUT MODE0, 816 cards, slow version
240*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530241static int pcl816_ai_insn_read(struct comedi_device *dev,
242 struct comedi_subdevice *s,
243 struct comedi_insn *insn, unsigned int *data)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800244{
245 int n;
246 int timeout;
247
248 DPRINTK("mode 0 analog input\n");
Bill Pemberton58c05762009-03-27 11:31:22 -0400249 /* software trigger, DMA and INT off */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800250 outb(0, dev->iobase + PCL816_CONTROL);
Bill Pemberton58c05762009-03-27 11:31:22 -0400251 /* clear INT (conversion end) flag */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800252 outb(0, dev->iobase + PCL816_CLRINT);
253
Bill Pemberton58c05762009-03-27 11:31:22 -0400254 /* Set the input channel */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800255 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
256 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */
257
258 for (n = 0; n < insn->n; n++) {
259
260 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
261
262 timeout = 100;
263 while (timeout--) {
264 if (!(inb(dev->iobase + PCL816_STATUS) &
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530265 PCL816_STATUS_DRDY_MASK)) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400266 /* return read value */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800267 data[n] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530268 ((inb(dev->iobase +
269 PCL816_AD_HI) << 8) |
270 (inb(dev->iobase + PCL816_AD_LO)));
Juan Grigeradd2996b2009-02-19 09:30:57 -0800271
272 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
273 break;
274 }
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700275 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800276 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400277 /* Return timeout error */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800278 if (!timeout) {
279 comedi_error(dev, "A/D insn timeout\n");
280 data[0] = 0;
281 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
282 return -EIO;
283 }
284
285 }
286 return n;
287}
288
289/*
290==============================================================================
291 analog input interrupt mode 1 & 3, 818 cards
292 one sample per interrupt version
293*/
294static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
295{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400296 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400297 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800298 int low, hi;
299 int timeout = 50; /* wait max 50us */
300
301 while (timeout--) {
302 if (!(inb(dev->iobase + PCL816_STATUS) &
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530303 PCL816_STATUS_DRDY_MASK))
Juan Grigeradd2996b2009-02-19 09:30:57 -0800304 break;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700305 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800306 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400307 if (!timeout) { /* timeout, bail error */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800308 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
309 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
310 pcl816_ai_cancel(dev, s);
311 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
312 comedi_event(dev, s);
313 return IRQ_HANDLED;
314
315 }
316
Bill Pemberton58c05762009-03-27 11:31:22 -0400317 /* get the sample */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800318 low = inb(dev->iobase + PCL816_AD_LO);
319 hi = inb(dev->iobase + PCL816_AD_HI);
320
321 comedi_buf_put(s->async, (hi << 8) | low);
322
323 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
324
325 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
326 devpriv->ai_act_chanlist_pos = 0;
327
Ian Abbott13de4f02010-01-20 13:04:45 +0000328 s->async->cur_chan++;
329 if (s->async->cur_chan >= devpriv->ai_n_chan) {
330 s->async->cur_chan = 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800331 devpriv->ai_act_scan++;
332 }
333
334 if (!devpriv->ai_neverending)
335 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
336 /* all data sampled */
337 pcl816_ai_cancel(dev, s);
338 s->async->events |= COMEDI_CB_EOA;
339 }
340 comedi_event(dev, s);
341 return IRQ_HANDLED;
342}
343
344/*
345==============================================================================
346 analog input dma mode 1 & 3, 816 cards
347*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530348static void transfer_from_dma_buf(struct comedi_device *dev,
349 struct comedi_subdevice *s, short *ptr,
350 unsigned int bufptr, unsigned int len)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800351{
352 int i;
353
354 s->async->events = 0;
355
356 for (i = 0; i < len; i++) {
357
358 comedi_buf_put(s->async, ptr[bufptr++]);
359
360 if (++devpriv->ai_act_chanlist_pos >=
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530361 devpriv->ai_act_chanlist_len) {
Juan Grigeradd2996b2009-02-19 09:30:57 -0800362 devpriv->ai_act_chanlist_pos = 0;
Ian Abbott13de4f02010-01-20 13:04:45 +0000363 }
364
365 s->async->cur_chan++;
366 if (s->async->cur_chan >= devpriv->ai_n_chan) {
367 s->async->cur_chan = 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800368 devpriv->ai_act_scan++;
369 }
370
371 if (!devpriv->ai_neverending)
Bill Pemberton58c05762009-03-27 11:31:22 -0400372 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800373 pcl816_ai_cancel(dev, s);
374 s->async->events |= COMEDI_CB_EOA;
375 s->async->events |= COMEDI_CB_BLOCK;
376 break;
377 }
378 }
379
380 comedi_event(dev, s);
381}
382
383static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
384{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400385 struct comedi_device *dev = d;
Bill Pemberton34c43922009-03-16 22:05:14 -0400386 struct comedi_subdevice *s = dev->subdevices + 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800387 int len, bufptr, this_dma_buf;
388 unsigned long dma_flags;
Bill Pemberton790c5542009-03-16 22:05:02 -0400389 short *ptr;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800390
391 disable_dma(devpriv->dma);
392 this_dma_buf = devpriv->next_dma_buf;
393
Bill Pemberton58c05762009-03-27 11:31:22 -0400394 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { /* switch dma bufs */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800395
396 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
397 set_dma_mode(devpriv->dma, DMA_MODE_READ);
398 dma_flags = claim_dma_lock();
Bill Pemberton58c05762009-03-27 11:31:22 -0400399/* clear_dma_ff (devpriv->dma); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800400 set_dma_addr(devpriv->dma,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530401 devpriv->hwdmaptr[devpriv->next_dma_buf]);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800402 if (devpriv->dma_runs_to_end) {
403 set_dma_count(devpriv->dma,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530404 devpriv->hwdmasize[devpriv->
405 next_dma_buf]);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800406 } else {
407 set_dma_count(devpriv->dma, devpriv->last_dma_run);
408 }
409 release_dma_lock(dma_flags);
410 enable_dma(devpriv->dma);
411 }
412
413 devpriv->dma_runs_to_end--;
414 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
415
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530416 ptr = (short *)devpriv->dmabuf[this_dma_buf];
Juan Grigeradd2996b2009-02-19 09:30:57 -0800417
418 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
419 bufptr = devpriv->ai_poll_ptr;
420 devpriv->ai_poll_ptr = 0;
421
422 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
423 return IRQ_HANDLED;
424}
425
426/*
427==============================================================================
428 INT procedure
429*/
Jiri Slaby70265d22009-03-26 09:34:06 +0100430static irqreturn_t interrupt_pcl816(int irq, void *d)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800431{
Bill Pemberton71b5f4f2009-03-16 22:05:08 -0400432 struct comedi_device *dev = d;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800433 DPRINTK("<I>");
434
435 if (!dev->attached) {
436 comedi_error(dev, "premature interrupt");
437 return IRQ_HANDLED;
438 }
439
440 switch (devpriv->int816_mode) {
441 case INT_TYPE_AI1_DMA:
442 case INT_TYPE_AI3_DMA:
443 return interrupt_pcl816_ai_mode13_dma(irq, d);
444 case INT_TYPE_AI1_INT:
445 case INT_TYPE_AI3_INT:
446 return interrupt_pcl816_ai_mode13_int(irq, d);
447 }
448
449 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
450 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530451 (!devpriv->int816_mode)) {
Juan Grigeradd2996b2009-02-19 09:30:57 -0800452 if (devpriv->irq_was_now_closed) {
453 devpriv->irq_was_now_closed = 0;
Bill Pemberton58c05762009-03-27 11:31:22 -0400454 /* comedi_error(dev,"last IRQ.."); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800455 return IRQ_HANDLED;
456 }
457 comedi_error(dev, "bad IRQ!");
458 return IRQ_NONE;
459 }
André Goddard Rosabbc9a992009-11-14 13:09:06 -0200460 comedi_error(dev, "IRQ from unknown source!");
Juan Grigeradd2996b2009-02-19 09:30:57 -0800461 return IRQ_NONE;
462}
463
464/*
465==============================================================================
466 COMMAND MODE
467*/
Bill Pembertonda91b262009-04-09 16:07:03 -0400468static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800469{
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700470 printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530471 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700472 printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530473 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700474 printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530475 cmd->scan_end_src);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700476 printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530477 cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800478}
479
480/*
481==============================================================================
482*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530483static int pcl816_ai_cmdtest(struct comedi_device *dev,
484 struct comedi_subdevice *s, struct comedi_cmd *cmd)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800485{
486 int err = 0;
Ian Abbott48b1aff2009-11-20 11:32:37 +0000487 int tmp, divisor1 = 0, divisor2 = 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800488
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530489 DEBUG(printk("pcl816 pcl812_ai_cmdtest\n"); pcl816_cmdtest_out(-1, cmd);
490 );
Juan Grigeradd2996b2009-02-19 09:30:57 -0800491
492 /* step 1: make sure trigger sources are trivially valid */
493 tmp = cmd->start_src;
494 cmd->start_src &= TRIG_NOW;
495 if (!cmd->start_src || tmp != cmd->start_src)
496 err++;
497
498 tmp = cmd->scan_begin_src;
499 cmd->scan_begin_src &= TRIG_FOLLOW;
500 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
501 err++;
502
Ian Abbottefdf83c2009-11-20 11:32:38 +0000503 tmp = cmd->convert_src;
504 cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
505 if (!cmd->convert_src || tmp != cmd->convert_src)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800506 err++;
507
508 tmp = cmd->scan_end_src;
509 cmd->scan_end_src &= TRIG_COUNT;
510 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
511 err++;
512
513 tmp = cmd->stop_src;
514 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
515 if (!cmd->stop_src || tmp != cmd->stop_src)
516 err++;
517
518 if (err) {
519 return 1;
520 }
521
522 /* step 2: make sure trigger sources are unique and mutually compatible */
523
524 if (cmd->start_src != TRIG_NOW) {
525 cmd->start_src = TRIG_NOW;
526 err++;
527 }
528
529 if (cmd->scan_begin_src != TRIG_FOLLOW) {
530 cmd->scan_begin_src = TRIG_FOLLOW;
531 err++;
532 }
533
534 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
535 cmd->convert_src = TRIG_TIMER;
536 err++;
537 }
538
539 if (cmd->scan_end_src != TRIG_COUNT) {
540 cmd->scan_end_src = TRIG_COUNT;
541 err++;
542 }
543
544 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
545 err++;
546
547 if (err) {
548 return 2;
549 }
550
551 /* step 3: make sure arguments are trivially compatible */
552 if (cmd->start_arg != 0) {
553 cmd->start_arg = 0;
554 err++;
555 }
556
557 if (cmd->scan_begin_arg != 0) {
558 cmd->scan_begin_arg = 0;
559 err++;
560 }
561 if (cmd->convert_src == TRIG_TIMER) {
562 if (cmd->convert_arg < this_board->ai_ns_min) {
563 cmd->convert_arg = this_board->ai_ns_min;
564 err++;
565 }
566 } else { /* TRIG_EXT */
567 if (cmd->convert_arg != 0) {
568 cmd->convert_arg = 0;
569 err++;
570 }
571 }
572
Juan Grigeradd2996b2009-02-19 09:30:57 -0800573 if (cmd->scan_end_arg != cmd->chanlist_len) {
574 cmd->scan_end_arg = cmd->chanlist_len;
575 err++;
576 }
577 if (cmd->stop_src == TRIG_COUNT) {
578 if (!cmd->stop_arg) {
579 cmd->stop_arg = 1;
580 err++;
581 }
582 } else { /* TRIG_NONE */
583 if (cmd->stop_arg != 0) {
584 cmd->stop_arg = 0;
585 err++;
586 }
587 }
588
589 if (err) {
590 return 3;
591 }
592
593 /* step 4: fix up any arguments */
594 if (cmd->convert_src == TRIG_TIMER) {
595 tmp = cmd->convert_arg;
596 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530597 &divisor1, &divisor2,
598 &cmd->convert_arg,
599 cmd->flags & TRIG_ROUND_MASK);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800600 if (cmd->convert_arg < this_board->ai_ns_min)
601 cmd->convert_arg = this_board->ai_ns_min;
602 if (tmp != cmd->convert_arg)
603 err++;
604 }
605
606 if (err) {
607 return 4;
608 }
609
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000610 /* step 5: complain about special chanlist considerations */
611
612 if (cmd->chanlist) {
613 if (!check_channel_list(dev, s, cmd->chanlist,
614 cmd->chanlist_len))
615 return 5; /* incorrect channels list */
616 }
617
Juan Grigeradd2996b2009-02-19 09:30:57 -0800618 return 0;
619}
620
Bill Pembertonda91b262009-04-09 16:07:03 -0400621static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800622{
623 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
Bill Pembertonea6d0d42009-03-16 22:05:47 -0400624 struct comedi_cmd *cmd = &s->async->cmd;
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000625 unsigned int seglen;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800626
627 if (cmd->start_src != TRIG_NOW)
628 return -EINVAL;
629 if (cmd->scan_begin_src != TRIG_FOLLOW)
630 return -EINVAL;
631 if (cmd->scan_end_src != TRIG_COUNT)
632 return -EINVAL;
633 if (cmd->scan_end_arg != cmd->chanlist_len)
634 return -EINVAL;
Bill Pemberton58c05762009-03-27 11:31:22 -0400635/* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800636 if (devpriv->irq_blocked)
637 return -EBUSY;
638
639 if (cmd->convert_src == TRIG_TIMER) {
640 if (cmd->convert_arg < this_board->ai_ns_min)
641 cmd->convert_arg = this_board->ai_ns_min;
642
643 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530644 &divisor2, &cmd->convert_arg,
645 cmd->flags & TRIG_ROUND_MASK);
Bill Pemberton58c05762009-03-27 11:31:22 -0400646 if (divisor1 == 1) { /* PCL816 crash if any divisor is set to 1 */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800647 divisor1 = 2;
648 divisor2 /= 2;
649 }
650 if (divisor2 == 1) {
651 divisor2 = 2;
652 divisor1 /= 2;
653 }
654 }
655
Bill Pemberton58c05762009-03-27 11:31:22 -0400656 start_pacer(dev, -1, 0, 0); /* stop pacer */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800657
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000658 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
659 if (seglen < 1)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800660 return -EINVAL;
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000661 setup_channel_list(dev, s, cmd->chanlist, seglen);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700662 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800663
Ian Abbott13de4f02010-01-20 13:04:45 +0000664 devpriv->ai_n_chan = cmd->chanlist_len;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800665 devpriv->ai_act_scan = 0;
666 s->async->cur_chan = 0;
667 devpriv->irq_blocked = 1;
668 devpriv->ai_poll_ptr = 0;
669 devpriv->irq_was_now_closed = 0;
670
671 if (cmd->stop_src == TRIG_COUNT) {
672 devpriv->ai_scans = cmd->stop_arg;
673 devpriv->ai_neverending = 0;
674 } else {
675 devpriv->ai_scans = 0;
676 devpriv->ai_neverending = 1;
677 }
678
Bill Pemberton58c05762009-03-27 11:31:22 -0400679 if ((cmd->flags & TRIG_WAKE_EOS)) { /* don't we want wake up every scan? */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800680 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
Bill Pemberton58c05762009-03-27 11:31:22 -0400681 /* devpriv->ai_eos=1; */
682 /* if (devpriv->ai_n_chan==1) */
683 /* devpriv->dma=0; // DMA is useless for this situation */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800684 }
685
686 if (devpriv->dma) {
687 bytes = devpriv->hwdmasize[0];
688 if (!devpriv->ai_neverending) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400689 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); /* how many */
690 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fill */
691 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800692 devpriv->dma_runs_to_end--;
693 if (devpriv->dma_runs_to_end >= 0)
694 bytes = devpriv->hwdmasize[0];
695 } else
696 devpriv->dma_runs_to_end = -1;
697
698 devpriv->next_dma_buf = 0;
699 set_dma_mode(devpriv->dma, DMA_MODE_READ);
700 dma_flags = claim_dma_lock();
701 clear_dma_ff(devpriv->dma);
702 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
703 set_dma_count(devpriv->dma, bytes);
704 release_dma_lock(dma_flags);
705 enable_dma(devpriv->dma);
706 }
707
708 start_pacer(dev, 1, divisor1, divisor2);
709 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
710
711 switch (cmd->convert_src) {
712 case TRIG_TIMER:
713 devpriv->int816_mode = INT_TYPE_AI1_DMA;
Bill Pemberton58c05762009-03-27 11:31:22 -0400714 outb(0x32, dev->iobase + PCL816_CONTROL); /* Pacer+IRQ+DMA */
715 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq and DMA to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800716 break;
717
718 default:
719 devpriv->int816_mode = INT_TYPE_AI3_DMA;
Bill Pemberton58c05762009-03-27 11:31:22 -0400720 outb(0x34, dev->iobase + PCL816_CONTROL); /* Ext trig+IRQ+DMA */
721 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800722 break;
723 }
724
725 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
726 return 0;
727}
728
Bill Pembertonda91b262009-04-09 16:07:03 -0400729static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800730{
731 unsigned long flags;
732 unsigned int top1, top2, i;
733
734 if (!devpriv->dma)
Bill Pemberton58c05762009-03-27 11:31:22 -0400735 return 0; /* poll is valid only for DMA transfer */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800736
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700737 spin_lock_irqsave(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800738
739 for (i = 0; i < 20; i++) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400740 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800741 top2 = get_dma_residue(devpriv->dma);
742 if (top1 == top2)
743 break;
744 }
745 if (top1 != top2) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700746 spin_unlock_irqrestore(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800747 return 0;
748 }
749
Bill Pemberton58c05762009-03-27 11:31:22 -0400750 top1 = devpriv->hwdmasize[0] - top1; /* where is now DMA in buffer */
751 top1 >>= 1; /* sample position */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800752 top2 = top1 - devpriv->ai_poll_ptr;
Bill Pemberton58c05762009-03-27 11:31:22 -0400753 if (top2 < 1) { /* no new samples */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700754 spin_unlock_irqrestore(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800755 return 0;
756 }
757
758 transfer_from_dma_buf(dev, s,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530759 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
760 devpriv->ai_poll_ptr, top2);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800761
Bill Pemberton58c05762009-03-27 11:31:22 -0400762 devpriv->ai_poll_ptr = top1; /* new buffer position */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700763 spin_unlock_irqrestore(&dev->spinlock, flags);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800764
765 return s->async->buf_write_count - s->async->buf_read_count;
766}
767
768/*
769==============================================================================
770 cancel any mode 1-4 AI
771*/
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530772static int pcl816_ai_cancel(struct comedi_device *dev,
773 struct comedi_subdevice *s)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800774{
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700775/* DEBUG(printk("pcl816_ai_cancel()\n");) */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800776
777 if (devpriv->irq_blocked > 0) {
778 switch (devpriv->int816_mode) {
779#ifdef unused
780 case INT_TYPE_AI1_DMA_RTC:
781 case INT_TYPE_AI3_DMA_RTC:
Bill Pemberton58c05762009-03-27 11:31:22 -0400782 set_rtc_irq_bit(0); /* stop RTC */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800783 del_timer(&devpriv->rtc_irq_timer);
784#endif
785 case INT_TYPE_AI1_DMA:
786 case INT_TYPE_AI3_DMA:
787 disable_dma(devpriv->dma);
788 case INT_TYPE_AI1_INT:
789 case INT_TYPE_AI3_INT:
790 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700791 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800792 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
793 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
794 outb(0x70, dev->iobase + PCL816_CTRCTL);
795 outb(0, dev->iobase + PCL816_AD_LO);
796 inb(dev->iobase + PCL816_AD_LO);
797 inb(dev->iobase + PCL816_AD_HI);
798 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
799 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
800 devpriv->irq_blocked = 0;
801 devpriv->irq_was_now_closed = devpriv->int816_mode;
802 devpriv->int816_mode = 0;
803 devpriv->last_int_sub = s;
Bill Pemberton58c05762009-03-27 11:31:22 -0400804/* s->busy = 0; */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800805 break;
806 }
807 }
808
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530809 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
810 return 0;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800811}
812
813/*
814==============================================================================
815 chech for PCL816
816*/
817static int pcl816_check(unsigned long iobase)
818{
819 outb(0x00, iobase + PCL816_MUX);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700820 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800821 if (inb(iobase + PCL816_MUX) != 0x00)
Bill Pemberton58c05762009-03-27 11:31:22 -0400822 return 1; /* there isn't card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800823 outb(0x55, iobase + PCL816_MUX);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700824 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800825 if (inb(iobase + PCL816_MUX) != 0x55)
Bill Pemberton58c05762009-03-27 11:31:22 -0400826 return 1; /* there isn't card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800827 outb(0x00, iobase + PCL816_MUX);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700828 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800829 outb(0x18, iobase + PCL816_CONTROL);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700830 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800831 if (inb(iobase + PCL816_CONTROL) != 0x18)
Bill Pemberton58c05762009-03-27 11:31:22 -0400832 return 1; /* there isn't card */
833 return 0; /* ok, card exist */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800834}
835
836/*
837==============================================================================
838 reset whole PCL-816 cards
839*/
Bill Pembertonda91b262009-04-09 16:07:03 -0400840static void pcl816_reset(struct comedi_device *dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800841{
Bill Pemberton58c05762009-03-27 11:31:22 -0400842/* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
843/* outb (0, dev->iobase + PCL818_DA_HI); */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700844/* udelay (1); */
Bill Pemberton58c05762009-03-27 11:31:22 -0400845/* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
846/* outb (0, dev->iobase + PCL818_DO_LO); */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700847/* udelay (1); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800848 outb(0, dev->iobase + PCL816_CONTROL);
849 outb(0, dev->iobase + PCL816_MUX);
850 outb(0, dev->iobase + PCL816_CLRINT);
851 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
852 outb(0x70, dev->iobase + PCL816_CTRCTL);
853 outb(0x30, dev->iobase + PCL816_CTRCTL);
854 outb(0, dev->iobase + PCL816_RANGE);
855}
856
857/*
858==============================================================================
859 Start/stop pacer onboard pacer
860*/
861static void
Bill Pembertonda91b262009-04-09 16:07:03 -0400862start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530863 unsigned int divisor2)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800864{
865 outb(0x32, dev->iobase + PCL816_CTRCTL);
866 outb(0xff, dev->iobase + PCL816_CTR0);
867 outb(0x00, dev->iobase + PCL816_CTR0);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700868 udelay(1);
Bill Pemberton58c05762009-03-27 11:31:22 -0400869 outb(0xb4, dev->iobase + PCL816_CTRCTL); /* set counter 2 as mode 3 */
870 outb(0x74, dev->iobase + PCL816_CTRCTL); /* set counter 1 as mode 3 */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700871 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800872
873 if (mode == 1) {
874 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
875 divisor2);
876 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
877 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
878 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
879 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
880 }
881
882 /* clear pending interrupts (just in case) */
Bill Pemberton58c05762009-03-27 11:31:22 -0400883/* outb(0, dev->iobase + PCL816_CLRINT); */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800884}
885
886/*
887==============================================================================
888 Check if channel list from user is builded correctly
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000889 If it's ok, then return non-zero length of repeated segment of channel list
Juan Grigeradd2996b2009-02-19 09:30:57 -0800890*/
891static int
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000892check_channel_list(struct comedi_device *dev,
893 struct comedi_subdevice *s, unsigned int *chanlist,
894 unsigned int chanlen)
Juan Grigeradd2996b2009-02-19 09:30:57 -0800895{
896 unsigned int chansegment[16];
897 unsigned int i, nowmustbechan, seglen, segpos;
898
Bill Pemberton58c05762009-03-27 11:31:22 -0400899 /* correct channel and range number check itself comedi/range.c */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800900 if (chanlen < 1) {
901 comedi_error(dev, "range/channel list is empty!");
902 return 0;
903 }
904
905 if (chanlen > 1) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400906 chansegment[0] = chanlist[0]; /* first channel is everytime ok */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800907 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400908 /* build part of chanlist */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700909 DEBUG(printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530910 CR_RANGE(chanlist[i]));)
911 if (chanlist[0] == chanlist[i])
Bill Pemberton58c05762009-03-27 11:31:22 -0400912 break; /* we detect loop, this must by finish */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800913 nowmustbechan =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530914 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
Juan Grigeradd2996b2009-02-19 09:30:57 -0800915 if (nowmustbechan != CR_CHAN(chanlist[i])) {
Bill Pemberton58c05762009-03-27 11:31:22 -0400916 /* channel list isn't continous :-( */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700917 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530918 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
919 dev->minor, i, CR_CHAN(chanlist[i]),
920 nowmustbechan, CR_CHAN(chanlist[0]));
Juan Grigeradd2996b2009-02-19 09:30:57 -0800921 return 0;
922 }
Bill Pemberton58c05762009-03-27 11:31:22 -0400923 chansegment[i] = chanlist[i]; /* well, this is next correct channel in list */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800924 }
925
Bill Pemberton58c05762009-03-27 11:31:22 -0400926 for (i = 0, segpos = 0; i < chanlen; i++) { /* check whole chanlist */
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700927 DEBUG(printk("%d %d=%d %d\n",
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530928 CR_CHAN(chansegment[i % seglen]),
929 CR_RANGE(chansegment[i % seglen]),
930 CR_CHAN(chanlist[i]),
931 CR_RANGE(chanlist[i]));)
932 if (chanlist[i] != chansegment[i % seglen]) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700933 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +0530934 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
935 dev->minor, i, CR_CHAN(chansegment[i]),
936 CR_RANGE(chansegment[i]),
937 CR_AREF(chansegment[i]),
938 CR_CHAN(chanlist[i % seglen]),
939 CR_RANGE(chanlist[i % seglen]),
940 CR_AREF(chansegment[i % seglen]));
Bill Pemberton58c05762009-03-27 11:31:22 -0400941 return 0; /* chan/gain list is strange */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800942 }
943 }
944 } else {
945 seglen = 1;
946 }
947
Ian Abbott64a1f7b2010-01-20 13:04:50 +0000948 return seglen; /* we can serve this with MUX logic */
949}
950
951/*
952==============================================================================
953 Program scan/gain logic with channel list.
954*/
955static void
956setup_channel_list(struct comedi_device *dev,
957 struct comedi_subdevice *s, unsigned int *chanlist,
958 unsigned int seglen)
959{
960 unsigned int i;
961
Juan Grigeradd2996b2009-02-19 09:30:57 -0800962 devpriv->ai_act_chanlist_len = seglen;
963 devpriv->ai_act_chanlist_pos = 0;
964
Bill Pemberton58c05762009-03-27 11:31:22 -0400965 for (i = 0; i < seglen; i++) { /* store range list to card */
Juan Grigeradd2996b2009-02-19 09:30:57 -0800966 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
967 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
968 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */
969 }
970
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -0700971 udelay(1);
Juan Grigeradd2996b2009-02-19 09:30:57 -0800972
973 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 -0800974}
975
976#ifdef unused
977/*
978==============================================================================
979 Enable(1)/disable(0) periodic interrupts from RTC
980*/
981static int set_rtc_irq_bit(unsigned char bit)
982{
983 unsigned char val;
984 unsigned long flags;
985
986 if (bit == 1) {
987 RTC_timer_lock++;
988 if (RTC_timer_lock > 1)
989 return 0;
990 } else {
991 RTC_timer_lock--;
992 if (RTC_timer_lock < 0)
993 RTC_timer_lock = 0;
994 if (RTC_timer_lock > 0)
995 return 0;
996 }
997
998 save_flags(flags);
999 cli();
1000 val = CMOS_READ(RTC_CONTROL);
1001 if (bit) {
1002 val |= RTC_PIE;
1003 } else {
1004 val &= ~RTC_PIE;
1005 }
1006 CMOS_WRITE(val, RTC_CONTROL);
1007 CMOS_READ(RTC_INTR_FLAGS);
1008 restore_flags(flags);
1009 return 0;
1010}
1011#endif
1012
1013/*
1014==============================================================================
1015 Free any resources that we have claimed
1016*/
Bill Pembertonda91b262009-04-09 16:07:03 -04001017static void free_resources(struct comedi_device *dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001018{
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001019 /* printk("free_resource()\n"); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001020 if (dev->private) {
1021 pcl816_ai_cancel(dev, devpriv->sub_ai);
1022 pcl816_reset(dev);
1023 if (devpriv->dma)
1024 free_dma(devpriv->dma);
1025 if (devpriv->dmabuf[0])
1026 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1027 if (devpriv->dmabuf[1])
1028 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1029#ifdef unused
1030 if (devpriv->rtc_irq)
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001031 free_irq(devpriv->rtc_irq, dev);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001032 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1033 if (devpriv->rtc_iobase)
1034 release_region(devpriv->rtc_iobase,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301035 devpriv->rtc_iosize);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001036 }
1037#endif
1038 }
1039
1040 if (dev->irq)
1041 free_irq(dev->irq, dev);
1042 if (dev->iobase)
1043 release_region(dev->iobase, this_board->io_range);
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001044 /* printk("free_resource() end\n"); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001045}
1046
1047/*
1048==============================================================================
1049
1050 Initialization
1051
1052*/
Bill Pembertonda91b262009-04-09 16:07:03 -04001053static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001054{
1055 int ret;
1056 unsigned long iobase;
1057 unsigned int irq, dma;
1058 unsigned long pages;
Bill Pemberton58c05762009-03-27 11:31:22 -04001059 /* int i; */
Bill Pemberton34c43922009-03-16 22:05:14 -04001060 struct comedi_subdevice *s;
Juan Grigeradd2996b2009-02-19 09:30:57 -08001061
1062 /* claim our I/O space */
1063 iobase = it->options[0];
1064 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301065 this_board->name, iobase);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001066
1067 if (!request_region(iobase, this_board->io_range, "pcl816")) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001068 printk("I/O port conflict\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001069 return -EIO;
1070 }
1071
1072 dev->iobase = iobase;
1073
1074 if (pcl816_check(iobase)) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001075 printk(", I cann't detect board. FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001076 return -EIO;
1077 }
1078
Bill Pembertonc3744132009-04-22 21:11:47 -04001079 ret = alloc_private(dev, sizeof(struct pcl816_private));
1080 if (ret < 0)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001081 return ret; /* Can't alloc mem */
1082
1083 /* set up some name stuff */
1084 dev->board_name = this_board->name;
1085
1086 /* grab our IRQ */
1087 irq = 0;
1088 if (this_board->IRQbits != 0) { /* board support IRQ */
1089 irq = it->options[1];
1090 if (irq) { /* we want to use IRQ */
1091 if (((1 << irq) & this_board->IRQbits) == 0) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001092 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301093 (", IRQ %u is out of allowed range, DISABLING IT",
1094 irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001095 irq = 0; /* Bad IRQ */
1096 } else {
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301097 if (request_irq
1098 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001099 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301100 (", unable to allocate IRQ %u, DISABLING IT",
1101 irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001102 irq = 0; /* Can't use IRQ */
1103 } else {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001104 printk(", irq=%u", irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001105 }
1106 }
1107 }
1108 }
1109
1110 dev->irq = irq;
1111 if (irq) {
1112 devpriv->irq_free = 1;
1113 } /* 1=we have allocated irq */
1114 else {
1115 devpriv->irq_free = 0;
1116 }
1117 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1118 devpriv->int816_mode = 0; /* mode of irq */
1119
1120#ifdef unused
1121 /* grab RTC for DMA operations */
1122 devpriv->dma_rtc = 0;
Bill Pemberton58c05762009-03-27 11:31:22 -04001123 if (it->options[2] > 0) { /* we want to use DMA */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001124 if (RTC_lock == 0) {
1125 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301126 "pcl816 (RTC)"))
Juan Grigeradd2996b2009-02-19 09:30:57 -08001127 goto no_rtc;
1128 }
1129 devpriv->rtc_iobase = RTC_PORT(0);
1130 devpriv->rtc_iosize = RTC_IO_EXTENT;
1131 RTC_lock++;
1132#ifdef UNTESTED_CODE
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001133 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301134 "pcl816 DMA (RTC)", dev)) {
Juan Grigeradd2996b2009-02-19 09:30:57 -08001135 devpriv->dma_rtc = 1;
1136 devpriv->rtc_irq = RTC_IRQ;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001137 printk(", dma_irq=%u", devpriv->rtc_irq);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001138 } else {
1139 RTC_lock--;
1140 if (RTC_lock == 0) {
1141 if (devpriv->rtc_iobase)
1142 release_region(devpriv->rtc_iobase,
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301143 devpriv->rtc_iosize);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001144 }
1145 devpriv->rtc_iobase = 0;
1146 devpriv->rtc_iosize = 0;
1147 }
1148#else
1149 printk("pcl816: RTC code missing");
1150#endif
1151
1152 }
1153
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301154no_rtc:
Juan Grigeradd2996b2009-02-19 09:30:57 -08001155#endif
1156 /* grab our DMA */
1157 dma = 0;
1158 devpriv->dma = dma;
1159 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1160 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1161
1162 if (this_board->DMAbits != 0) { /* board support DMA */
1163 dma = it->options[2];
1164 if (dma < 1)
1165 goto no_dma; /* DMA disabled */
1166
1167 if (((1 << dma) & this_board->DMAbits) == 0) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001168 printk(", DMA is out of allowed range, FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001169 return -EINVAL; /* Bad DMA */
1170 }
1171 ret = request_dma(dma, "pcl816");
1172 if (ret) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001173 printk(", unable to allocate DMA %u, FAIL!\n", dma);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001174 return -EBUSY; /* DMA isn't free */
1175 }
1176
1177 devpriv->dma = dma;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001178 printk(", dma=%u", dma);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001179 pages = 2; /* we need 16KB */
1180 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1181
1182 if (!devpriv->dmabuf[0]) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001183 printk(", unable to allocate DMA buffer, FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001184 /* maybe experiment with try_to_free_pages() will help .... */
1185 return -EBUSY; /* no buffer :-( */
1186 }
1187 devpriv->dmapages[0] = pages;
1188 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1189 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001190 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001191
Bill Pemberton58c05762009-03-27 11:31:22 -04001192 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001193 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1194 if (!devpriv->dmabuf[1]) {
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001195 printk
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301196 (", unable to allocate DMA buffer, FAIL!\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001197 return -EBUSY;
1198 }
1199 devpriv->dmapages[1] = pages;
1200 devpriv->hwdmaptr[1] =
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301201 virt_to_bus((void *)devpriv->dmabuf[1]);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001202 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1203 }
1204 }
1205
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301206no_dma:
Juan Grigeradd2996b2009-02-19 09:30:57 -08001207
1208/* if (this_board->n_aochan > 0)
1209 subdevs[1] = COMEDI_SUBD_AO;
1210 if (this_board->n_dichan > 0)
1211 subdevs[2] = COMEDI_SUBD_DI;
1212 if (this_board->n_dochan > 0)
1213 subdevs[3] = COMEDI_SUBD_DO;
1214*/
Bill Pembertonc3744132009-04-22 21:11:47 -04001215
1216 ret = alloc_subdevices(dev, 1);
1217 if (ret < 0)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001218 return ret;
1219
1220 s = dev->subdevices + 0;
1221 if (this_board->n_aichan > 0) {
1222 s->type = COMEDI_SUBD_AI;
1223 devpriv->sub_ai = s;
1224 dev->read_subdev = s;
1225 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1226 s->n_chan = this_board->n_aichan;
1227 s->subdev_flags |= SDF_DIFF;
Bill Pemberton58c05762009-03-27 11:31:22 -04001228 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
Juan Grigeradd2996b2009-02-19 09:30:57 -08001229 s->maxdata = this_board->ai_maxdata;
1230 s->len_chanlist = this_board->ai_chanlist;
1231 s->range_table = this_board->ai_range_type;
1232 s->cancel = pcl816_ai_cancel;
1233 s->do_cmdtest = pcl816_ai_cmdtest;
1234 s->do_cmd = pcl816_ai_cmd;
1235 s->poll = pcl816_ai_poll;
1236 s->insn_read = pcl816_ai_insn_read;
1237 } else {
1238 s->type = COMEDI_SUBD_UNUSED;
1239 }
1240
1241#if 0
1242case COMEDI_SUBD_AO:
1243 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1244 s->n_chan = this_board->n_aochan;
1245 s->maxdata = this_board->ao_maxdata;
1246 s->len_chanlist = this_board->ao_chanlist;
1247 s->range_table = this_board->ao_range_type;
1248 break;
1249
1250case COMEDI_SUBD_DI:
1251 s->subdev_flags = SDF_READABLE;
1252 s->n_chan = this_board->n_dichan;
1253 s->maxdata = 1;
1254 s->len_chanlist = this_board->n_dichan;
1255 s->range_table = &range_digital;
1256 break;
1257
1258case COMEDI_SUBD_DO:
1259 s->subdev_flags = SDF_WRITABLE;
1260 s->n_chan = this_board->n_dochan;
1261 s->maxdata = 1;
1262 s->len_chanlist = this_board->n_dochan;
1263 s->range_table = &range_digital;
1264 break;
1265#endif
1266
1267 pcl816_reset(dev);
1268
Greg Kroah-Hartman5f74ea12009-04-27 14:44:31 -07001269 printk("\n");
Juan Grigeradd2996b2009-02-19 09:30:57 -08001270
1271 return 0;
1272}
1273
1274/*
1275==============================================================================
1276 Removes device
1277 */
Bill Pembertonda91b262009-04-09 16:07:03 -04001278static int pcl816_detach(struct comedi_device *dev)
Juan Grigeradd2996b2009-02-19 09:30:57 -08001279{
Mithlesh Thukral0a85b6f2009-06-08 21:04:41 +05301280 DEBUG(printk("comedi%d: pcl816: remove\n", dev->minor);)
1281 free_resources(dev);
Juan Grigeradd2996b2009-02-19 09:30:57 -08001282#ifdef unused
1283 if (devpriv->dma_rtc)
1284 RTC_lock--;
1285#endif
1286 return 0;
1287}