1/*
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 <linux/module.h>
36#include "../comedidev.h"
37
38#include <linux/gfp.h>
39#include <linux/delay.h>
40#include <linux/io.h>
41#include <linux/interrupt.h>
42#include <asm/dma.h>
43
44#include "comedi_fc.h"
45#include "8253.h"
46
47/*
48 * Register I/O map
49 */
50#define PCL816_DO_DI_LSB_REG			0x00
51#define PCL816_DO_DI_MSB_REG			0x01
52#define PCL816_TIMER_BASE			0x04
53#define PCL816_AI_LSB_REG			0x08
54#define PCL816_AI_MSB_REG			0x09
55#define PCL816_RANGE_REG			0x09
56#define PCL816_CLRINT_REG			0x0a
57#define PCL816_MUX_REG				0x0b
58#define PCL816_MUX_SCAN(_first, _last)		(((_last) << 4) | (_first))
59#define PCL816_CTRL_REG				0x0c
60#define PCL816_CTRL_DISABLE_TRIG		(0 << 0)
61#define PCL816_CTRL_SOFT_TRIG			(1 << 0)
62#define PCL816_CTRL_PACER_TRIG			(1 << 1)
63#define PCL816_CTRL_EXT_TRIG			(1 << 2)
64#define PCL816_CTRL_POE				(1 << 3)
65#define PCL816_CTRL_DMAEN			(1 << 4)
66#define PCL816_CTRL_INTEN			(1 << 5)
67#define PCL816_CTRL_DMASRC_SLOT0		(0 << 6)
68#define PCL816_CTRL_DMASRC_SLOT1		(1 << 6)
69#define PCL816_CTRL_DMASRC_SLOT2		(2 << 6)
70#define PCL816_STATUS_REG			0x0d
71#define PCL816_STATUS_NEXT_CHAN_MASK		(0xf << 0)
72#define PCL816_STATUS_INTSRC_MASK		(3 << 4)
73#define PCL816_STATUS_INTSRC_SLOT0		(0 << 4)
74#define PCL816_STATUS_INTSRC_SLOT1		(1 << 4)
75#define PCL816_STATUS_INTSRC_SLOT2		(2 << 4)
76#define PCL816_STATUS_INTSRC_DMA		(3 << 4)
77#define PCL816_STATUS_INTACT			(1 << 6)
78#define PCL816_STATUS_DRDY			(1 << 7)
79
80#define MAGIC_DMA_WORD 0x5a5a
81
82static const struct comedi_lrange range_pcl816 = {
83	8, {
84		BIP_RANGE(10),
85		BIP_RANGE(5),
86		BIP_RANGE(2.5),
87		BIP_RANGE(1.25),
88		UNI_RANGE(10),
89		UNI_RANGE(5),
90		UNI_RANGE(2.5),
91		UNI_RANGE(1.25)
92	}
93};
94
95struct pcl816_board {
96	const char *name;
97	int ai_maxdata;
98	int ao_maxdata;
99	int ai_chanlist;
100};
101
102static const struct pcl816_board boardtypes[] = {
103	{
104		.name		= "pcl816",
105		.ai_maxdata	= 0xffff,
106		.ao_maxdata	= 0xffff,
107		.ai_chanlist	= 1024,
108	}, {
109		.name		= "pcl814b",
110		.ai_maxdata	= 0x3fff,
111		.ao_maxdata	= 0x3fff,
112		.ai_chanlist	= 1024,
113	},
114};
115
116struct pcl816_private {
117	unsigned int dma;	/*  used DMA, 0=don't use DMA */
118	unsigned int dmapages;
119	unsigned int hwdmasize;
120	unsigned long dmabuf[2];	/*  pointers to begin of DMA buffers */
121	unsigned int hwdmaptr[2];	/*  hardware address of DMA buffers */
122	int next_dma_buf;	/*  which DMA buffer will be used next round */
123	long dma_runs_to_end;	/*  how many we must permorm DMA transfer to end of record */
124	unsigned long last_dma_run;	/*  how many bytes we must transfer on last DMA page */
125	int ai_act_scan;	/*  how many scans we finished */
126	unsigned int ai_poll_ptr;	/*  how many sampes transfer poll */
127	unsigned int divisor1;
128	unsigned int divisor2;
129	unsigned int ai_cmd_running:1;
130	unsigned int ai_cmd_canceled:1;
131};
132
133static void pcl816_start_pacer(struct comedi_device *dev, bool load_counters)
134{
135	struct pcl816_private *devpriv = dev->private;
136	unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
137
138	i8254_set_mode(timer_base, 0, 0, I8254_MODE1 | I8254_BINARY);
139	i8254_write(timer_base, 0, 0, 0x00ff);
140	udelay(1);
141
142	i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
143	i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
144	udelay(1);
145
146	if (load_counters) {
147		i8254_write(timer_base, 0, 2, devpriv->divisor2);
148		i8254_write(timer_base, 0, 1, devpriv->divisor1);
149	}
150}
151
152static void pcl816_ai_setup_dma(struct comedi_device *dev,
153				struct comedi_subdevice *s)
154{
155	struct pcl816_private *devpriv = dev->private;
156	struct comedi_cmd *cmd = &s->async->cmd;
157	unsigned int dma_flags;
158	unsigned int bytes;
159
160	bytes = devpriv->hwdmasize;
161	if (cmd->stop_src == TRIG_COUNT) {
162		/*  how many */
163		bytes = cmd->stop_arg * cfc_bytes_per_scan(s);
164
165		/*  how many DMA pages we must fill */
166		devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize;
167
168		/* on last dma transfer must be moved */
169		devpriv->last_dma_run = bytes % devpriv->hwdmasize;
170		devpriv->dma_runs_to_end--;
171		if (devpriv->dma_runs_to_end >= 0)
172			bytes = devpriv->hwdmasize;
173	} else
174		devpriv->dma_runs_to_end = -1;
175
176	devpriv->next_dma_buf = 0;
177	set_dma_mode(devpriv->dma, DMA_MODE_READ);
178	dma_flags = claim_dma_lock();
179	clear_dma_ff(devpriv->dma);
180	set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
181	set_dma_count(devpriv->dma, bytes);
182	release_dma_lock(dma_flags);
183	enable_dma(devpriv->dma);
184}
185
186static void pcl816_ai_setup_next_dma(struct comedi_device *dev,
187				     struct comedi_subdevice *s)
188{
189	struct pcl816_private *devpriv = dev->private;
190	struct comedi_cmd *cmd = &s->async->cmd;
191	unsigned long dma_flags;
192
193	disable_dma(devpriv->dma);
194	if (devpriv->dma_runs_to_end > -1 || cmd->stop_src == TRIG_NONE) {
195		/* switch dma bufs */
196		devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
197		set_dma_mode(devpriv->dma, DMA_MODE_READ);
198		dma_flags = claim_dma_lock();
199		set_dma_addr(devpriv->dma,
200			     devpriv->hwdmaptr[devpriv->next_dma_buf]);
201		if (devpriv->dma_runs_to_end)
202			set_dma_count(devpriv->dma, devpriv->hwdmasize);
203		else
204			set_dma_count(devpriv->dma, devpriv->last_dma_run);
205		release_dma_lock(dma_flags);
206		enable_dma(devpriv->dma);
207	}
208
209	devpriv->dma_runs_to_end--;
210}
211
212static void pcl816_ai_set_chan_range(struct comedi_device *dev,
213				     unsigned int chan,
214				     unsigned int range)
215{
216	outb(chan, dev->iobase + PCL816_MUX_REG);
217	outb(range, dev->iobase + PCL816_RANGE_REG);
218}
219
220static void pcl816_ai_set_chan_scan(struct comedi_device *dev,
221				    unsigned int first_chan,
222				    unsigned int last_chan)
223{
224	outb(PCL816_MUX_SCAN(first_chan, last_chan),
225	     dev->iobase + PCL816_MUX_REG);
226}
227
228static void pcl816_ai_setup_chanlist(struct comedi_device *dev,
229				     unsigned int *chanlist,
230				     unsigned int seglen)
231{
232	unsigned int first_chan = CR_CHAN(chanlist[0]);
233	unsigned int last_chan;
234	unsigned int range;
235	unsigned int i;
236
237	/* store range list to card */
238	for (i = 0; i < seglen; i++) {
239		last_chan = CR_CHAN(chanlist[i]);
240		range = CR_RANGE(chanlist[i]);
241
242		pcl816_ai_set_chan_range(dev, last_chan, range);
243	}
244
245	udelay(1);
246
247	pcl816_ai_set_chan_scan(dev, first_chan, last_chan);
248}
249
250static void pcl816_ai_clear_eoc(struct comedi_device *dev)
251{
252	/* writing any value clears the interrupt request */
253	outb(0, dev->iobase + PCL816_CLRINT_REG);
254}
255
256static void pcl816_ai_soft_trig(struct comedi_device *dev)
257{
258	/* writing any value triggers a software conversion */
259	outb(0, dev->iobase + PCL816_AI_LSB_REG);
260}
261
262static unsigned int pcl816_ai_get_sample(struct comedi_device *dev,
263					 struct comedi_subdevice *s)
264{
265	unsigned int val;
266
267	val = inb(dev->iobase + PCL816_AI_MSB_REG) << 8;
268	val |= inb(dev->iobase + PCL816_AI_LSB_REG);
269
270	return val & s->maxdata;
271}
272
273static int pcl816_ai_eoc(struct comedi_device *dev,
274			 struct comedi_subdevice *s,
275			 struct comedi_insn *insn,
276			 unsigned long context)
277{
278	unsigned int status;
279
280	status = inb(dev->iobase + PCL816_STATUS_REG);
281	if ((status & PCL816_STATUS_DRDY) == 0)
282		return 0;
283	return -EBUSY;
284}
285
286static bool pcl816_ai_next_chan(struct comedi_device *dev,
287				struct comedi_subdevice *s)
288{
289	struct pcl816_private *devpriv = dev->private;
290	struct comedi_cmd *cmd = &s->async->cmd;
291
292	s->async->events |= COMEDI_CB_BLOCK;
293
294	s->async->cur_chan++;
295	if (s->async->cur_chan >= cmd->chanlist_len) {
296		s->async->cur_chan = 0;
297		devpriv->ai_act_scan++;
298		s->async->events |= COMEDI_CB_EOS;
299	}
300
301	if (cmd->stop_src == TRIG_COUNT &&
302	    devpriv->ai_act_scan >= cmd->stop_arg) {
303		/* all data sampled */
304		s->async->events |= COMEDI_CB_EOA;
305		return false;
306	}
307
308	return true;
309}
310
311static void transfer_from_dma_buf(struct comedi_device *dev,
312				  struct comedi_subdevice *s,
313				  unsigned short *ptr,
314				  unsigned int bufptr, unsigned int len)
315{
316	int i;
317
318	for (i = 0; i < len; i++) {
319		comedi_buf_put(s, ptr[bufptr++]);
320
321		if (!pcl816_ai_next_chan(dev, s))
322			return;
323	}
324}
325
326static irqreturn_t pcl816_interrupt(int irq, void *d)
327{
328	struct comedi_device *dev = d;
329	struct comedi_subdevice *s = dev->read_subdev;
330	struct pcl816_private *devpriv = dev->private;
331	unsigned short *ptr;
332	unsigned int bufptr;
333	unsigned int len;
334
335	if (!dev->attached || !devpriv->ai_cmd_running) {
336		pcl816_ai_clear_eoc(dev);
337		return IRQ_HANDLED;
338	}
339
340	if (devpriv->ai_cmd_canceled) {
341		devpriv->ai_cmd_canceled = 0;
342		pcl816_ai_clear_eoc(dev);
343		return IRQ_HANDLED;
344	}
345
346	ptr = (unsigned short *)devpriv->dmabuf[devpriv->next_dma_buf];
347
348	pcl816_ai_setup_next_dma(dev, s);
349
350	len = (devpriv->hwdmasize >> 1) - devpriv->ai_poll_ptr;
351	bufptr = devpriv->ai_poll_ptr;
352	devpriv->ai_poll_ptr = 0;
353
354	transfer_from_dma_buf(dev, s, ptr, bufptr, len);
355
356	pcl816_ai_clear_eoc(dev);
357
358	cfc_handle_events(dev, s);
359	return IRQ_HANDLED;
360}
361
362static int check_channel_list(struct comedi_device *dev,
363			      struct comedi_subdevice *s,
364			      unsigned int *chanlist,
365			      unsigned int chanlen)
366{
367	unsigned int chansegment[16];
368	unsigned int i, nowmustbechan, seglen, segpos;
369
370	/*  correct channel and range number check itself comedi/range.c */
371	if (chanlen < 1) {
372		dev_err(dev->class_dev, "range/channel list is empty!\n");
373		return 0;
374	}
375
376	if (chanlen > 1) {
377		/*  first channel is every time ok */
378		chansegment[0] = chanlist[0];
379		for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
380			/*  we detect loop, this must by finish */
381			    if (chanlist[0] == chanlist[i])
382				break;
383			nowmustbechan =
384			    (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
385			if (nowmustbechan != CR_CHAN(chanlist[i])) {
386				/*  channel list isn't continuous :-( */
387				dev_dbg(dev->class_dev,
388					"channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
389					i, CR_CHAN(chanlist[i]), nowmustbechan,
390					CR_CHAN(chanlist[0]));
391				return 0;
392			}
393			/*  well, this is next correct channel in list */
394			chansegment[i] = chanlist[i];
395		}
396
397		/*  check whole chanlist */
398		for (i = 0, segpos = 0; i < chanlen; i++) {
399			    if (chanlist[i] != chansegment[i % seglen]) {
400				dev_dbg(dev->class_dev,
401					"bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
402					i, CR_CHAN(chansegment[i]),
403					CR_RANGE(chansegment[i]),
404					CR_AREF(chansegment[i]),
405					CR_CHAN(chanlist[i % seglen]),
406					CR_RANGE(chanlist[i % seglen]),
407					CR_AREF(chansegment[i % seglen]));
408				return 0;	/*  chan/gain list is strange */
409			}
410		}
411	} else {
412		seglen = 1;
413	}
414
415	return seglen;	/*  we can serve this with MUX logic */
416}
417
418static int pcl816_ai_cmdtest(struct comedi_device *dev,
419			     struct comedi_subdevice *s, struct comedi_cmd *cmd)
420{
421	struct pcl816_private *devpriv = dev->private;
422	int err = 0;
423	unsigned int arg;
424
425	/* Step 1 : check if triggers are trivially valid */
426
427	err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
428	err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
429	err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_EXT | TRIG_TIMER);
430	err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
431	err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
432
433	if (err)
434		return 1;
435
436	/* Step 2a : make sure trigger sources are unique */
437
438	err |= cfc_check_trigger_is_unique(cmd->convert_src);
439	err |= cfc_check_trigger_is_unique(cmd->stop_src);
440
441	/* Step 2b : and mutually compatible */
442
443	if (err)
444		return 2;
445
446
447	/* Step 3: check if arguments are trivially valid */
448
449	err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
450	err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
451
452	if (cmd->convert_src == TRIG_TIMER)
453		err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
454	else	/* TRIG_EXT */
455		err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
456
457	err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
458
459	if (cmd->stop_src == TRIG_COUNT)
460		err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
461	else	/* TRIG_NONE */
462		err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
463
464	if (err)
465		return 3;
466
467
468	/* step 4: fix up any arguments */
469	if (cmd->convert_src == TRIG_TIMER) {
470		arg = cmd->convert_arg;
471		i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
472					  &devpriv->divisor1,
473					  &devpriv->divisor2,
474					  &arg, cmd->flags);
475		err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
476	}
477
478	if (err)
479		return 4;
480
481
482	/* step 5: complain about special chanlist considerations */
483
484	if (cmd->chanlist) {
485		if (!check_channel_list(dev, s, cmd->chanlist,
486					cmd->chanlist_len))
487			return 5;	/*  incorrect channels list */
488	}
489
490	return 0;
491}
492
493static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
494{
495	struct pcl816_private *devpriv = dev->private;
496	struct comedi_cmd *cmd = &s->async->cmd;
497	unsigned int ctrl;
498	unsigned int seglen;
499
500	if (devpriv->ai_cmd_running)
501		return -EBUSY;
502
503	pcl816_start_pacer(dev, false);
504
505	seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
506	if (seglen < 1)
507		return -EINVAL;
508	pcl816_ai_setup_chanlist(dev, cmd->chanlist, seglen);
509	udelay(1);
510
511	devpriv->ai_act_scan = 0;
512	s->async->cur_chan = 0;
513	devpriv->ai_cmd_running = 1;
514	devpriv->ai_poll_ptr = 0;
515	devpriv->ai_cmd_canceled = 0;
516
517	pcl816_ai_setup_dma(dev, s);
518
519	pcl816_start_pacer(dev, true);
520
521	ctrl = PCL816_CTRL_INTEN | PCL816_CTRL_DMAEN | PCL816_CTRL_DMASRC_SLOT0;
522	if (cmd->convert_src == TRIG_TIMER)
523		ctrl |= PCL816_CTRL_PACER_TRIG;
524	else	/* TRIG_EXT */
525		ctrl |= PCL816_CTRL_EXT_TRIG;
526
527	outb(ctrl, dev->iobase + PCL816_CTRL_REG);
528	outb((devpriv->dma << 4) | dev->irq, dev->iobase + PCL816_STATUS_REG);
529
530	return 0;
531}
532
533static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
534{
535	struct pcl816_private *devpriv = dev->private;
536	unsigned long flags;
537	unsigned int top1, top2, i;
538
539	spin_lock_irqsave(&dev->spinlock, flags);
540
541	for (i = 0; i < 20; i++) {
542		top1 = get_dma_residue(devpriv->dma);	/*  where is now DMA */
543		top2 = get_dma_residue(devpriv->dma);
544		if (top1 == top2)
545			break;
546	}
547	if (top1 != top2) {
548		spin_unlock_irqrestore(&dev->spinlock, flags);
549		return 0;
550	}
551
552	/*  where is now DMA in buffer */
553	top1 = devpriv->hwdmasize - top1;
554	top1 >>= 1;		/*  sample position */
555	top2 = top1 - devpriv->ai_poll_ptr;
556	if (top2 < 1) {		/*  no new samples */
557		spin_unlock_irqrestore(&dev->spinlock, flags);
558		return 0;
559	}
560
561	transfer_from_dma_buf(dev, s,
562			      (unsigned short *)devpriv->dmabuf[devpriv->
563								next_dma_buf],
564			      devpriv->ai_poll_ptr, top2);
565
566	devpriv->ai_poll_ptr = top1;	/*  new buffer position */
567	spin_unlock_irqrestore(&dev->spinlock, flags);
568
569	cfc_handle_events(dev, s);
570
571	return comedi_buf_n_bytes_ready(s);
572}
573
574static int pcl816_ai_cancel(struct comedi_device *dev,
575			    struct comedi_subdevice *s)
576{
577	struct pcl816_private *devpriv = dev->private;
578
579	if (!devpriv->ai_cmd_running)
580		return 0;
581
582	outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
583	pcl816_ai_clear_eoc(dev);
584
585	/* Stop pacer */
586	i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
587			2, I8254_MODE0 | I8254_BINARY);
588	i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
589			1, I8254_MODE0 | I8254_BINARY);
590
591	devpriv->ai_cmd_running = 0;
592	devpriv->ai_cmd_canceled = 1;
593
594	return 0;
595}
596
597static int pcl816_ai_insn_read(struct comedi_device *dev,
598			       struct comedi_subdevice *s,
599			       struct comedi_insn *insn,
600			       unsigned int *data)
601{
602	unsigned int chan = CR_CHAN(insn->chanspec);
603	unsigned int range = CR_RANGE(insn->chanspec);
604	int ret = 0;
605	int i;
606
607	outb(PCL816_CTRL_SOFT_TRIG, dev->iobase + PCL816_CTRL_REG);
608
609	pcl816_ai_set_chan_range(dev, chan, range);
610	pcl816_ai_set_chan_scan(dev, chan, chan);
611
612	for (i = 0; i < insn->n; i++) {
613		pcl816_ai_clear_eoc(dev);
614		pcl816_ai_soft_trig(dev);
615
616		ret = comedi_timeout(dev, s, insn, pcl816_ai_eoc, 0);
617		if (ret)
618			break;
619
620		data[i] = pcl816_ai_get_sample(dev, s);
621	}
622	outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
623	pcl816_ai_clear_eoc(dev);
624
625	return ret ? ret : insn->n;
626}
627
628static int pcl816_di_insn_bits(struct comedi_device *dev,
629			       struct comedi_subdevice *s,
630			       struct comedi_insn *insn,
631			       unsigned int *data)
632{
633	data[1] = inb(dev->iobase + PCL816_DO_DI_LSB_REG) |
634		  (inb(dev->iobase + PCL816_DO_DI_MSB_REG) << 8);
635
636	return insn->n;
637}
638
639static int pcl816_do_insn_bits(struct comedi_device *dev,
640			       struct comedi_subdevice *s,
641			       struct comedi_insn *insn,
642			       unsigned int *data)
643{
644	if (comedi_dio_update_state(s, data)) {
645		outb(s->state & 0xff, dev->iobase + PCL816_DO_DI_LSB_REG);
646		outb((s->state >> 8), dev->iobase + PCL816_DO_DI_MSB_REG);
647	}
648
649	data[1] = s->state;
650
651	return insn->n;
652}
653
654static void pcl816_reset(struct comedi_device *dev)
655{
656	unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
657
658	outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
659	pcl816_ai_set_chan_range(dev, 0, 0);
660	pcl816_ai_clear_eoc(dev);
661
662	/* Stop pacer */
663	i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
664	i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
665	i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
666
667	/* set all digital outputs low */
668	outb(0, dev->iobase + PCL816_DO_DI_LSB_REG);
669	outb(0, dev->iobase + PCL816_DO_DI_MSB_REG);
670}
671
672static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
673{
674	const struct pcl816_board *board = dev->board_ptr;
675	struct pcl816_private *devpriv;
676	struct comedi_subdevice *s;
677	int ret;
678	int i;
679
680	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
681	if (!devpriv)
682		return -ENOMEM;
683
684	ret = comedi_request_region(dev, it->options[0], 0x10);
685	if (ret)
686		return ret;
687
688	/* we can use IRQ 2-7 for async command support */
689	if (it->options[1] >= 2 && it->options[1] <= 7) {
690		ret = request_irq(it->options[1], pcl816_interrupt, 0,
691				  dev->board_name, dev);
692		if (ret == 0)
693			dev->irq = it->options[1];
694	}
695
696	/* we need an IRQ to do DMA on channel 3 or 1 */
697	if (dev->irq && (it->options[2] == 3 || it->options[2] == 1)) {
698		ret = request_dma(it->options[2], dev->board_name);
699		if (ret) {
700			dev_err(dev->class_dev,
701				"unable to request DMA channel %d\n",
702				it->options[2]);
703			return -EBUSY;
704		}
705		devpriv->dma = it->options[2];
706
707		devpriv->dmapages = 2;	/* we need 16KB */
708		devpriv->hwdmasize = (1 << devpriv->dmapages) * PAGE_SIZE;
709
710		for (i = 0; i < 2; i++) {
711			unsigned long dmabuf;
712
713			dmabuf = __get_dma_pages(GFP_KERNEL, devpriv->dmapages);
714			if (!dmabuf)
715				return -ENOMEM;
716
717			devpriv->dmabuf[i] = dmabuf;
718			devpriv->hwdmaptr[i] = virt_to_bus((void *)dmabuf);
719		}
720	}
721
722	ret = comedi_alloc_subdevices(dev, 4);
723	if (ret)
724		return ret;
725
726	s = &dev->subdevices[0];
727	s->type		= COMEDI_SUBD_AI;
728	s->subdev_flags	= SDF_CMD_READ | SDF_DIFF;
729	s->n_chan	= 16;
730	s->maxdata	= board->ai_maxdata;
731	s->range_table	= &range_pcl816;
732	s->insn_read	= pcl816_ai_insn_read;
733	if (devpriv->dma) {
734		dev->read_subdev = s;
735		s->subdev_flags	|= SDF_CMD_READ;
736		s->len_chanlist	= board->ai_chanlist;
737		s->do_cmdtest	= pcl816_ai_cmdtest;
738		s->do_cmd	= pcl816_ai_cmd;
739		s->poll		= pcl816_ai_poll;
740		s->cancel	= pcl816_ai_cancel;
741	}
742
743	/* Analog OUtput subdevice */
744	s = &dev->subdevices[2];
745	s->type		= COMEDI_SUBD_UNUSED;
746#if 0
747	subdevs[1] = COMEDI_SUBD_AO;
748	s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
749	s->n_chan = 1;
750	s->maxdata = board->ao_maxdata;
751	s->range_table = &range_pcl816;
752#endif
753
754	/* Digital Input subdevice */
755	s = &dev->subdevices[2];
756	s->type		= COMEDI_SUBD_DI;
757	s->subdev_flags	= SDF_READABLE;
758	s->n_chan	= 16;
759	s->maxdata	= 1;
760	s->range_table	= &range_digital;
761	s->insn_bits	= pcl816_di_insn_bits;
762
763	/* Digital Output subdevice */
764	s = &dev->subdevices[3];
765	s->type		= COMEDI_SUBD_DO;
766	s->subdev_flags	= SDF_WRITABLE;
767	s->n_chan	= 16;
768	s->maxdata	= 1;
769	s->range_table	= &range_digital;
770	s->insn_bits	= pcl816_do_insn_bits;
771
772	pcl816_reset(dev);
773
774	return 0;
775}
776
777static void pcl816_detach(struct comedi_device *dev)
778{
779	struct pcl816_private *devpriv = dev->private;
780
781	if (dev->private) {
782		pcl816_ai_cancel(dev, dev->read_subdev);
783		pcl816_reset(dev);
784		if (devpriv->dma)
785			free_dma(devpriv->dma);
786		if (devpriv->dmabuf[0])
787			free_pages(devpriv->dmabuf[0], devpriv->dmapages);
788		if (devpriv->dmabuf[1])
789			free_pages(devpriv->dmabuf[1], devpriv->dmapages);
790	}
791	comedi_legacy_detach(dev);
792}
793
794static struct comedi_driver pcl816_driver = {
795	.driver_name	= "pcl816",
796	.module		= THIS_MODULE,
797	.attach		= pcl816_attach,
798	.detach		= pcl816_detach,
799	.board_name	= &boardtypes[0].name,
800	.num_names	= ARRAY_SIZE(boardtypes),
801	.offset		= sizeof(struct pcl816_board),
802};
803module_comedi_driver(pcl816_driver);
804
805MODULE_AUTHOR("Comedi http://www.comedi.org");
806MODULE_DESCRIPTION("Comedi low-level driver");
807MODULE_LICENSE("GPL");
808