[go: nahoru, domu]

1/*
2    comedi/drivers/amplc_dio200_common.c
3
4    Common support code for "amplc_dio200" and "amplc_dio200_pci".
5
6    Copyright (C) 2005-2013 MEV Ltd. <http://www.mev.co.uk/>
7
8    COMEDI - Linux Control and Measurement Device Interface
9    Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20*/
21
22#include <linux/module.h>
23#include <linux/interrupt.h>
24
25#include "../comedidev.h"
26
27#include "amplc_dio200.h"
28#include "comedi_fc.h"
29#include "8253.h"
30#include "8255.h"		/* only for register defines */
31
32/* 200 series registers */
33#define DIO200_IO_SIZE		0x20
34#define DIO200_PCIE_IO_SIZE	0x4000
35#define DIO200_XCLK_SCE		0x18	/* Group X clock selection register */
36#define DIO200_YCLK_SCE		0x19	/* Group Y clock selection register */
37#define DIO200_ZCLK_SCE		0x1a	/* Group Z clock selection register */
38#define DIO200_XGAT_SCE		0x1b	/* Group X gate selection register */
39#define DIO200_YGAT_SCE		0x1c	/* Group Y gate selection register */
40#define DIO200_ZGAT_SCE		0x1d	/* Group Z gate selection register */
41#define DIO200_INT_SCE		0x1e	/* Interrupt enable/status register */
42/* Extra registers for new PCIe boards */
43#define DIO200_ENHANCE		0x20	/* 1 to enable enhanced features */
44#define DIO200_VERSION		0x24	/* Hardware version register */
45#define DIO200_TS_CONFIG	0x600	/* Timestamp timer config register */
46#define DIO200_TS_COUNT		0x602	/* Timestamp timer count register */
47
48/*
49 * Functions for constructing value for DIO_200_?CLK_SCE and
50 * DIO_200_?GAT_SCE registers:
51 *
52 * 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2.
53 * 'chan' is the channel: 0, 1 or 2.
54 * 'source' is the signal source: 0 to 7, or 0 to 31 for "enhanced" boards.
55 */
56static unsigned char clk_gat_sce(unsigned int which, unsigned int chan,
57				 unsigned int source)
58{
59	return (which << 5) | (chan << 3) |
60	       ((source & 030) << 3) | (source & 007);
61}
62
63static unsigned char clk_sce(unsigned int which, unsigned int chan,
64			     unsigned int source)
65{
66	return clk_gat_sce(which, chan, source);
67}
68
69static unsigned char gat_sce(unsigned int which, unsigned int chan,
70			     unsigned int source)
71{
72	return clk_gat_sce(which, chan, source);
73}
74
75/*
76 * Periods of the internal clock sources in nanoseconds.
77 */
78static const unsigned int clock_period[32] = {
79	[1] = 100,		/* 10 MHz */
80	[2] = 1000,		/* 1 MHz */
81	[3] = 10000,		/* 100 kHz */
82	[4] = 100000,		/* 10 kHz */
83	[5] = 1000000,		/* 1 kHz */
84	[11] = 50,		/* 20 MHz (enhanced boards) */
85	/* clock sources 12 and later reserved for enhanced boards */
86};
87
88/*
89 * Timestamp timer configuration register (for new PCIe boards).
90 */
91#define TS_CONFIG_RESET		0x100	/* Reset counter to zero. */
92#define TS_CONFIG_CLK_SRC_MASK	0x0FF	/* Clock source. */
93#define TS_CONFIG_MAX_CLK_SRC	2	/* Maximum clock source value. */
94
95/*
96 * Periods of the timestamp timer clock sources in nanoseconds.
97 */
98static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = {
99	1,			/* 1 nanosecond (but with 20 ns granularity). */
100	1000,			/* 1 microsecond. */
101	1000000,		/* 1 millisecond. */
102};
103
104struct dio200_subdev_8254 {
105	unsigned int ofs;		/* Counter base offset */
106	unsigned int clk_sce_ofs;	/* CLK_SCE base address */
107	unsigned int gat_sce_ofs;	/* GAT_SCE base address */
108	int which;			/* Bit 5 of CLK_SCE or GAT_SCE */
109	unsigned int clock_src[3];	/* Current clock sources */
110	unsigned int gate_src[3];	/* Current gate sources */
111	spinlock_t spinlock;
112};
113
114struct dio200_subdev_8255 {
115	unsigned int ofs;		/* DIO base offset */
116};
117
118struct dio200_subdev_intr {
119	spinlock_t spinlock;
120	unsigned int ofs;
121	unsigned int valid_isns;
122	unsigned int enabled_isns;
123	unsigned int stopcount;
124	bool active:1;
125};
126
127static unsigned char dio200_read8(struct comedi_device *dev,
128				  unsigned int offset)
129{
130	const struct dio200_board *board = dev->board_ptr;
131
132	if (board->is_pcie)
133		offset <<= 3;
134
135	if (dev->mmio)
136		return readb(dev->mmio + offset);
137	return inb(dev->iobase + offset);
138}
139
140static void dio200_write8(struct comedi_device *dev,
141			  unsigned int offset, unsigned char val)
142{
143	const struct dio200_board *board = dev->board_ptr;
144
145	if (board->is_pcie)
146		offset <<= 3;
147
148	if (dev->mmio)
149		writeb(val, dev->mmio + offset);
150	else
151		outb(val, dev->iobase + offset);
152}
153
154static unsigned int dio200_read32(struct comedi_device *dev,
155				  unsigned int offset)
156{
157	const struct dio200_board *board = dev->board_ptr;
158
159	if (board->is_pcie)
160		offset <<= 3;
161
162	if (dev->mmio)
163		return readl(dev->mmio + offset);
164	return inl(dev->iobase + offset);
165}
166
167static void dio200_write32(struct comedi_device *dev,
168			   unsigned int offset, unsigned int val)
169{
170	const struct dio200_board *board = dev->board_ptr;
171
172	if (board->is_pcie)
173		offset <<= 3;
174
175	if (dev->mmio)
176		writel(val, dev->mmio + offset);
177	else
178		outl(val, dev->iobase + offset);
179}
180
181static int dio200_subdev_intr_insn_bits(struct comedi_device *dev,
182					struct comedi_subdevice *s,
183					struct comedi_insn *insn,
184					unsigned int *data)
185{
186	const struct dio200_board *board = dev->board_ptr;
187	struct dio200_subdev_intr *subpriv = s->private;
188
189	if (board->has_int_sce) {
190		/* Just read the interrupt status register.  */
191		data[1] = dio200_read8(dev, subpriv->ofs) & subpriv->valid_isns;
192	} else {
193		/* No interrupt status register. */
194		data[0] = 0;
195	}
196
197	return insn->n;
198}
199
200static void dio200_stop_intr(struct comedi_device *dev,
201			     struct comedi_subdevice *s)
202{
203	const struct dio200_board *board = dev->board_ptr;
204	struct dio200_subdev_intr *subpriv = s->private;
205
206	subpriv->active = false;
207	subpriv->enabled_isns = 0;
208	if (board->has_int_sce)
209		dio200_write8(dev, subpriv->ofs, 0);
210}
211
212static void dio200_start_intr(struct comedi_device *dev,
213			      struct comedi_subdevice *s)
214{
215	const struct dio200_board *board = dev->board_ptr;
216	struct dio200_subdev_intr *subpriv = s->private;
217	struct comedi_cmd *cmd = &s->async->cmd;
218	unsigned int n;
219	unsigned isn_bits;
220
221	/* Determine interrupt sources to enable. */
222	isn_bits = 0;
223	if (cmd->chanlist) {
224		for (n = 0; n < cmd->chanlist_len; n++)
225			isn_bits |= (1U << CR_CHAN(cmd->chanlist[n]));
226	}
227	isn_bits &= subpriv->valid_isns;
228	/* Enable interrupt sources. */
229	subpriv->enabled_isns = isn_bits;
230	if (board->has_int_sce)
231		dio200_write8(dev, subpriv->ofs, isn_bits);
232}
233
234static int dio200_inttrig_start_intr(struct comedi_device *dev,
235				     struct comedi_subdevice *s,
236				     unsigned int trig_num)
237{
238	struct dio200_subdev_intr *subpriv = s->private;
239	struct comedi_cmd *cmd = &s->async->cmd;
240	unsigned long flags;
241
242	if (trig_num != cmd->start_arg)
243		return -EINVAL;
244
245	spin_lock_irqsave(&subpriv->spinlock, flags);
246	s->async->inttrig = NULL;
247	if (subpriv->active)
248		dio200_start_intr(dev, s);
249
250	spin_unlock_irqrestore(&subpriv->spinlock, flags);
251
252	return 1;
253}
254
255static void dio200_read_scan_intr(struct comedi_device *dev,
256				  struct comedi_subdevice *s,
257				  unsigned int triggered)
258{
259	struct dio200_subdev_intr *subpriv = s->private;
260	struct comedi_cmd *cmd = &s->async->cmd;
261	unsigned short val;
262	unsigned int n, ch;
263
264	val = 0;
265	for (n = 0; n < cmd->chanlist_len; n++) {
266		ch = CR_CHAN(cmd->chanlist[n]);
267		if (triggered & (1U << ch))
268			val |= (1U << n);
269	}
270	/* Write the scan to the buffer. */
271	if (comedi_buf_put(s, val)) {
272		s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
273	} else {
274		/* Error!  Stop acquisition.  */
275		dio200_stop_intr(dev, s);
276		s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW;
277		dev_err(dev->class_dev, "buffer overflow\n");
278	}
279
280	/* Check for end of acquisition. */
281	if (cmd->stop_src == TRIG_COUNT) {
282		if (subpriv->stopcount > 0) {
283			subpriv->stopcount--;
284			if (subpriv->stopcount == 0) {
285				s->async->events |= COMEDI_CB_EOA;
286				dio200_stop_intr(dev, s);
287			}
288		}
289	}
290}
291
292static int dio200_handle_read_intr(struct comedi_device *dev,
293				   struct comedi_subdevice *s)
294{
295	const struct dio200_board *board = dev->board_ptr;
296	struct dio200_subdev_intr *subpriv = s->private;
297	unsigned triggered;
298	unsigned intstat;
299	unsigned cur_enabled;
300	unsigned int oldevents;
301	unsigned long flags;
302
303	triggered = 0;
304
305	spin_lock_irqsave(&subpriv->spinlock, flags);
306	oldevents = s->async->events;
307	if (board->has_int_sce) {
308		/*
309		 * Collect interrupt sources that have triggered and disable
310		 * them temporarily.  Loop around until no extra interrupt
311		 * sources have triggered, at which point, the valid part of
312		 * the interrupt status register will read zero, clearing the
313		 * cause of the interrupt.
314		 *
315		 * Mask off interrupt sources already seen to avoid infinite
316		 * loop in case of misconfiguration.
317		 */
318		cur_enabled = subpriv->enabled_isns;
319		while ((intstat = (dio200_read8(dev, subpriv->ofs) &
320				   subpriv->valid_isns & ~triggered)) != 0) {
321			triggered |= intstat;
322			cur_enabled &= ~triggered;
323			dio200_write8(dev, subpriv->ofs, cur_enabled);
324		}
325	} else {
326		/*
327		 * No interrupt status register.  Assume the single interrupt
328		 * source has triggered.
329		 */
330		triggered = subpriv->enabled_isns;
331	}
332
333	if (triggered) {
334		/*
335		 * Some interrupt sources have triggered and have been
336		 * temporarily disabled to clear the cause of the interrupt.
337		 *
338		 * Reenable them NOW to minimize the time they are disabled.
339		 */
340		cur_enabled = subpriv->enabled_isns;
341		if (board->has_int_sce)
342			dio200_write8(dev, subpriv->ofs, cur_enabled);
343
344		if (subpriv->active) {
345			/*
346			 * The command is still active.
347			 *
348			 * Ignore interrupt sources that the command isn't
349			 * interested in (just in case there's a race
350			 * condition).
351			 */
352			if (triggered & subpriv->enabled_isns)
353				/* Collect scan data. */
354				dio200_read_scan_intr(dev, s, triggered);
355		}
356	}
357	spin_unlock_irqrestore(&subpriv->spinlock, flags);
358
359	if (oldevents != s->async->events)
360		comedi_event(dev, s);
361
362	return (triggered != 0);
363}
364
365static int dio200_subdev_intr_cancel(struct comedi_device *dev,
366				     struct comedi_subdevice *s)
367{
368	struct dio200_subdev_intr *subpriv = s->private;
369	unsigned long flags;
370
371	spin_lock_irqsave(&subpriv->spinlock, flags);
372	if (subpriv->active)
373		dio200_stop_intr(dev, s);
374
375	spin_unlock_irqrestore(&subpriv->spinlock, flags);
376
377	return 0;
378}
379
380static int dio200_subdev_intr_cmdtest(struct comedi_device *dev,
381				      struct comedi_subdevice *s,
382				      struct comedi_cmd *cmd)
383{
384	int err = 0;
385
386	/* Step 1 : check if triggers are trivially valid */
387
388	err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
389	err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
390	err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
391	err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
392	err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
393
394	if (err)
395		return 1;
396
397	/* Step 2a : make sure trigger sources are unique */
398
399	err |= cfc_check_trigger_is_unique(cmd->start_src);
400	err |= cfc_check_trigger_is_unique(cmd->stop_src);
401
402	/* Step 2b : and mutually compatible */
403
404	if (err)
405		return 2;
406
407	/* Step 3: check if arguments are trivially valid */
408
409	err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
410	err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
411	err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
412	err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
413
414	if (cmd->stop_src == TRIG_COUNT)
415		err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
416	else	/* TRIG_NONE */
417		err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
418
419	if (err)
420		return 3;
421
422	/* step 4: fix up any arguments */
423
424	/* if (err) return 4; */
425
426	return 0;
427}
428
429static int dio200_subdev_intr_cmd(struct comedi_device *dev,
430				  struct comedi_subdevice *s)
431{
432	struct comedi_cmd *cmd = &s->async->cmd;
433	struct dio200_subdev_intr *subpriv = s->private;
434	unsigned long flags;
435
436	spin_lock_irqsave(&subpriv->spinlock, flags);
437
438	subpriv->active = true;
439	subpriv->stopcount = cmd->stop_arg;
440
441	if (cmd->start_src == TRIG_INT)
442		s->async->inttrig = dio200_inttrig_start_intr;
443	else	/* TRIG_NOW */
444		dio200_start_intr(dev, s);
445
446	spin_unlock_irqrestore(&subpriv->spinlock, flags);
447
448	return 0;
449}
450
451static int dio200_subdev_intr_init(struct comedi_device *dev,
452				   struct comedi_subdevice *s,
453				   unsigned int offset,
454				   unsigned valid_isns)
455{
456	const struct dio200_board *board = dev->board_ptr;
457	struct dio200_subdev_intr *subpriv;
458
459	subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
460	if (!subpriv)
461		return -ENOMEM;
462
463	subpriv->ofs = offset;
464	subpriv->valid_isns = valid_isns;
465	spin_lock_init(&subpriv->spinlock);
466
467	if (board->has_int_sce)
468		/* Disable interrupt sources. */
469		dio200_write8(dev, subpriv->ofs, 0);
470
471	s->type = COMEDI_SUBD_DI;
472	s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
473	if (board->has_int_sce) {
474		s->n_chan = DIO200_MAX_ISNS;
475		s->len_chanlist = DIO200_MAX_ISNS;
476	} else {
477		/* No interrupt source register.  Support single channel. */
478		s->n_chan = 1;
479		s->len_chanlist = 1;
480	}
481	s->range_table = &range_digital;
482	s->maxdata = 1;
483	s->insn_bits = dio200_subdev_intr_insn_bits;
484	s->do_cmdtest = dio200_subdev_intr_cmdtest;
485	s->do_cmd = dio200_subdev_intr_cmd;
486	s->cancel = dio200_subdev_intr_cancel;
487
488	return 0;
489}
490
491static irqreturn_t dio200_interrupt(int irq, void *d)
492{
493	struct comedi_device *dev = d;
494	struct comedi_subdevice *s = dev->read_subdev;
495	int handled;
496
497	if (!dev->attached)
498		return IRQ_NONE;
499
500	handled = dio200_handle_read_intr(dev, s);
501
502	return IRQ_RETVAL(handled);
503}
504
505static unsigned int dio200_subdev_8254_read_chan(struct comedi_device *dev,
506						 struct comedi_subdevice *s,
507						 unsigned int chan)
508{
509	struct dio200_subdev_8254 *subpriv = s->private;
510	unsigned int val;
511
512	/* latch counter */
513	val = chan << 6;
514	dio200_write8(dev, subpriv->ofs + i8254_control_reg, val);
515	/* read lsb, msb */
516	val = dio200_read8(dev, subpriv->ofs + chan);
517	val += dio200_read8(dev, subpriv->ofs + chan) << 8;
518	return val;
519}
520
521static void dio200_subdev_8254_write_chan(struct comedi_device *dev,
522					  struct comedi_subdevice *s,
523					  unsigned int chan,
524					  unsigned int count)
525{
526	struct dio200_subdev_8254 *subpriv = s->private;
527
528	/* write lsb, msb */
529	dio200_write8(dev, subpriv->ofs + chan, count & 0xff);
530	dio200_write8(dev, subpriv->ofs + chan, (count >> 8) & 0xff);
531}
532
533static void dio200_subdev_8254_set_mode(struct comedi_device *dev,
534					struct comedi_subdevice *s,
535					unsigned int chan,
536					unsigned int mode)
537{
538	struct dio200_subdev_8254 *subpriv = s->private;
539	unsigned int byte;
540
541	byte = chan << 6;
542	byte |= 0x30;		/* access order: lsb, msb */
543	byte |= (mode & 0xf);	/* counter mode and BCD|binary */
544	dio200_write8(dev, subpriv->ofs + i8254_control_reg, byte);
545}
546
547static unsigned int dio200_subdev_8254_status(struct comedi_device *dev,
548					      struct comedi_subdevice *s,
549					      unsigned int chan)
550{
551	struct dio200_subdev_8254 *subpriv = s->private;
552
553	/* latch status */
554	dio200_write8(dev, subpriv->ofs + i8254_control_reg,
555		      0xe0 | (2 << chan));
556	/* read status */
557	return dio200_read8(dev, subpriv->ofs + chan);
558}
559
560static int dio200_subdev_8254_read(struct comedi_device *dev,
561				   struct comedi_subdevice *s,
562				   struct comedi_insn *insn,
563				   unsigned int *data)
564{
565	struct dio200_subdev_8254 *subpriv = s->private;
566	int chan = CR_CHAN(insn->chanspec);
567	unsigned int n;
568	unsigned long flags;
569
570	for (n = 0; n < insn->n; n++) {
571		spin_lock_irqsave(&subpriv->spinlock, flags);
572		data[n] = dio200_subdev_8254_read_chan(dev, s, chan);
573		spin_unlock_irqrestore(&subpriv->spinlock, flags);
574	}
575	return insn->n;
576}
577
578static int dio200_subdev_8254_write(struct comedi_device *dev,
579				    struct comedi_subdevice *s,
580				    struct comedi_insn *insn,
581				    unsigned int *data)
582{
583	struct dio200_subdev_8254 *subpriv = s->private;
584	int chan = CR_CHAN(insn->chanspec);
585	unsigned int n;
586	unsigned long flags;
587
588	for (n = 0; n < insn->n; n++) {
589		spin_lock_irqsave(&subpriv->spinlock, flags);
590		dio200_subdev_8254_write_chan(dev, s, chan, data[n]);
591		spin_unlock_irqrestore(&subpriv->spinlock, flags);
592	}
593	return insn->n;
594}
595
596static int dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
597					   struct comedi_subdevice *s,
598					   unsigned int counter_number,
599					   unsigned int gate_src)
600{
601	const struct dio200_board *board = dev->board_ptr;
602	struct dio200_subdev_8254 *subpriv = s->private;
603	unsigned char byte;
604
605	if (!board->has_clk_gat_sce)
606		return -1;
607	if (counter_number > 2)
608		return -1;
609	if (gate_src > (board->is_pcie ? 31 : 7))
610		return -1;
611
612	subpriv->gate_src[counter_number] = gate_src;
613	byte = gat_sce(subpriv->which, counter_number, gate_src);
614	dio200_write8(dev, subpriv->gat_sce_ofs, byte);
615
616	return 0;
617}
618
619static int dio200_subdev_8254_get_gate_src(struct comedi_device *dev,
620					   struct comedi_subdevice *s,
621					   unsigned int counter_number)
622{
623	const struct dio200_board *board = dev->board_ptr;
624	struct dio200_subdev_8254 *subpriv = s->private;
625
626	if (!board->has_clk_gat_sce)
627		return -1;
628	if (counter_number > 2)
629		return -1;
630
631	return subpriv->gate_src[counter_number];
632}
633
634static int dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
635					    struct comedi_subdevice *s,
636					    unsigned int counter_number,
637					    unsigned int clock_src)
638{
639	const struct dio200_board *board = dev->board_ptr;
640	struct dio200_subdev_8254 *subpriv = s->private;
641	unsigned char byte;
642
643	if (!board->has_clk_gat_sce)
644		return -1;
645	if (counter_number > 2)
646		return -1;
647	if (clock_src > (board->is_pcie ? 31 : 7))
648		return -1;
649
650	subpriv->clock_src[counter_number] = clock_src;
651	byte = clk_sce(subpriv->which, counter_number, clock_src);
652	dio200_write8(dev, subpriv->clk_sce_ofs, byte);
653
654	return 0;
655}
656
657static int dio200_subdev_8254_get_clock_src(struct comedi_device *dev,
658					    struct comedi_subdevice *s,
659					    unsigned int counter_number,
660					    unsigned int *period_ns)
661{
662	const struct dio200_board *board = dev->board_ptr;
663	struct dio200_subdev_8254 *subpriv = s->private;
664	unsigned clock_src;
665
666	if (!board->has_clk_gat_sce)
667		return -1;
668	if (counter_number > 2)
669		return -1;
670
671	clock_src = subpriv->clock_src[counter_number];
672	*period_ns = clock_period[clock_src];
673	return clock_src;
674}
675
676static int dio200_subdev_8254_config(struct comedi_device *dev,
677				     struct comedi_subdevice *s,
678				     struct comedi_insn *insn,
679				     unsigned int *data)
680{
681	struct dio200_subdev_8254 *subpriv = s->private;
682	int ret = 0;
683	int chan = CR_CHAN(insn->chanspec);
684	unsigned long flags;
685
686	spin_lock_irqsave(&subpriv->spinlock, flags);
687	switch (data[0]) {
688	case INSN_CONFIG_SET_COUNTER_MODE:
689		if (data[1] > (I8254_MODE5 | I8254_BCD))
690			ret = -EINVAL;
691		else
692			dio200_subdev_8254_set_mode(dev, s, chan, data[1]);
693		break;
694	case INSN_CONFIG_8254_READ_STATUS:
695		data[1] = dio200_subdev_8254_status(dev, s, chan);
696		break;
697	case INSN_CONFIG_SET_GATE_SRC:
698		ret = dio200_subdev_8254_set_gate_src(dev, s, chan, data[2]);
699		if (ret < 0)
700			ret = -EINVAL;
701		break;
702	case INSN_CONFIG_GET_GATE_SRC:
703		ret = dio200_subdev_8254_get_gate_src(dev, s, chan);
704		if (ret < 0) {
705			ret = -EINVAL;
706			break;
707		}
708		data[2] = ret;
709		break;
710	case INSN_CONFIG_SET_CLOCK_SRC:
711		ret = dio200_subdev_8254_set_clock_src(dev, s, chan, data[1]);
712		if (ret < 0)
713			ret = -EINVAL;
714		break;
715	case INSN_CONFIG_GET_CLOCK_SRC:
716		ret = dio200_subdev_8254_get_clock_src(dev, s, chan, &data[2]);
717		if (ret < 0) {
718			ret = -EINVAL;
719			break;
720		}
721		data[1] = ret;
722		break;
723	default:
724		ret = -EINVAL;
725		break;
726	}
727	spin_unlock_irqrestore(&subpriv->spinlock, flags);
728	return ret < 0 ? ret : insn->n;
729}
730
731static int dio200_subdev_8254_init(struct comedi_device *dev,
732				   struct comedi_subdevice *s,
733				   unsigned int offset)
734{
735	const struct dio200_board *board = dev->board_ptr;
736	struct dio200_subdev_8254 *subpriv;
737	unsigned int chan;
738
739	subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
740	if (!subpriv)
741		return -ENOMEM;
742
743	s->type = COMEDI_SUBD_COUNTER;
744	s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
745	s->n_chan = 3;
746	s->maxdata = 0xFFFF;
747	s->insn_read = dio200_subdev_8254_read;
748	s->insn_write = dio200_subdev_8254_write;
749	s->insn_config = dio200_subdev_8254_config;
750
751	spin_lock_init(&subpriv->spinlock);
752	subpriv->ofs = offset;
753	if (board->has_clk_gat_sce) {
754		/* Derive CLK_SCE and GAT_SCE register offsets from
755		 * 8254 offset. */
756		subpriv->clk_sce_ofs = DIO200_XCLK_SCE + (offset >> 3);
757		subpriv->gat_sce_ofs = DIO200_XGAT_SCE + (offset >> 3);
758		subpriv->which = (offset >> 2) & 1;
759	}
760
761	/* Initialize channels. */
762	for (chan = 0; chan < 3; chan++) {
763		dio200_subdev_8254_set_mode(dev, s, chan,
764					    I8254_MODE0 | I8254_BINARY);
765		if (board->has_clk_gat_sce) {
766			/* Gate source 0 is VCC (logic 1). */
767			dio200_subdev_8254_set_gate_src(dev, s, chan, 0);
768			/* Clock source 0 is the dedicated clock input. */
769			dio200_subdev_8254_set_clock_src(dev, s, chan, 0);
770		}
771	}
772
773	return 0;
774}
775
776static void dio200_subdev_8255_set_dir(struct comedi_device *dev,
777				       struct comedi_subdevice *s)
778{
779	struct dio200_subdev_8255 *subpriv = s->private;
780	int config;
781
782	config = I8255_CTRL_CW;
783	/* 1 in io_bits indicates output, 1 in config indicates input */
784	if (!(s->io_bits & 0x0000ff))
785		config |= I8255_CTRL_A_IO;
786	if (!(s->io_bits & 0x00ff00))
787		config |= I8255_CTRL_B_IO;
788	if (!(s->io_bits & 0x0f0000))
789		config |= I8255_CTRL_C_LO_IO;
790	if (!(s->io_bits & 0xf00000))
791		config |= I8255_CTRL_C_HI_IO;
792	dio200_write8(dev, subpriv->ofs + I8255_CTRL_REG, config);
793}
794
795static int dio200_subdev_8255_bits(struct comedi_device *dev,
796				   struct comedi_subdevice *s,
797				   struct comedi_insn *insn,
798				   unsigned int *data)
799{
800	struct dio200_subdev_8255 *subpriv = s->private;
801	unsigned int mask;
802	unsigned int val;
803
804	mask = comedi_dio_update_state(s, data);
805	if (mask) {
806		if (mask & 0xff)
807			dio200_write8(dev, subpriv->ofs + I8255_DATA_A_REG,
808				      s->state & 0xff);
809		if (mask & 0xff00)
810			dio200_write8(dev, subpriv->ofs + I8255_DATA_B_REG,
811				      (s->state >> 8) & 0xff);
812		if (mask & 0xff0000)
813			dio200_write8(dev, subpriv->ofs + I8255_DATA_C_REG,
814				      (s->state >> 16) & 0xff);
815	}
816
817	val = dio200_read8(dev, subpriv->ofs + I8255_DATA_A_REG);
818	val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_B_REG) << 8;
819	val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_C_REG) << 16;
820
821	data[1] = val;
822
823	return insn->n;
824}
825
826static int dio200_subdev_8255_config(struct comedi_device *dev,
827				     struct comedi_subdevice *s,
828				     struct comedi_insn *insn,
829				     unsigned int *data)
830{
831	unsigned int chan = CR_CHAN(insn->chanspec);
832	unsigned int mask;
833	int ret;
834
835	if (chan < 8)
836		mask = 0x0000ff;
837	else if (chan < 16)
838		mask = 0x00ff00;
839	else if (chan < 20)
840		mask = 0x0f0000;
841	else
842		mask = 0xf00000;
843
844	ret = comedi_dio_insn_config(dev, s, insn, data, mask);
845	if (ret)
846		return ret;
847
848	dio200_subdev_8255_set_dir(dev, s);
849
850	return insn->n;
851}
852
853static int dio200_subdev_8255_init(struct comedi_device *dev,
854				   struct comedi_subdevice *s,
855				   unsigned int offset)
856{
857	struct dio200_subdev_8255 *subpriv;
858
859	subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
860	if (!subpriv)
861		return -ENOMEM;
862
863	subpriv->ofs = offset;
864
865	s->type = COMEDI_SUBD_DIO;
866	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
867	s->n_chan = 24;
868	s->range_table = &range_digital;
869	s->maxdata = 1;
870	s->insn_bits = dio200_subdev_8255_bits;
871	s->insn_config = dio200_subdev_8255_config;
872	dio200_subdev_8255_set_dir(dev, s);
873	return 0;
874}
875
876static int dio200_subdev_timer_read(struct comedi_device *dev,
877				    struct comedi_subdevice *s,
878				    struct comedi_insn *insn,
879				    unsigned int *data)
880{
881	unsigned int n;
882
883	for (n = 0; n < insn->n; n++)
884		data[n] = dio200_read32(dev, DIO200_TS_COUNT);
885	return n;
886}
887
888static void dio200_subdev_timer_reset(struct comedi_device *dev,
889				      struct comedi_subdevice *s)
890{
891	unsigned int clock;
892
893	clock = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
894	dio200_write32(dev, DIO200_TS_CONFIG, clock | TS_CONFIG_RESET);
895	dio200_write32(dev, DIO200_TS_CONFIG, clock);
896}
897
898static void dio200_subdev_timer_get_clock_src(struct comedi_device *dev,
899					      struct comedi_subdevice *s,
900					      unsigned int *src,
901					      unsigned int *period)
902{
903	unsigned int clk;
904
905	clk = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
906	*src = clk;
907	*period = (clk < ARRAY_SIZE(ts_clock_period)) ?
908		  ts_clock_period[clk] : 0;
909}
910
911static int dio200_subdev_timer_set_clock_src(struct comedi_device *dev,
912					     struct comedi_subdevice *s,
913					     unsigned int src)
914{
915	if (src > TS_CONFIG_MAX_CLK_SRC)
916		return -EINVAL;
917	dio200_write32(dev, DIO200_TS_CONFIG, src);
918	return 0;
919}
920
921static int dio200_subdev_timer_config(struct comedi_device *dev,
922				      struct comedi_subdevice *s,
923				      struct comedi_insn *insn,
924				      unsigned int *data)
925{
926	int ret = 0;
927
928	switch (data[0]) {
929	case INSN_CONFIG_RESET:
930		dio200_subdev_timer_reset(dev, s);
931		break;
932	case INSN_CONFIG_SET_CLOCK_SRC:
933		ret = dio200_subdev_timer_set_clock_src(dev, s, data[1]);
934		if (ret < 0)
935			ret = -EINVAL;
936		break;
937	case INSN_CONFIG_GET_CLOCK_SRC:
938		dio200_subdev_timer_get_clock_src(dev, s, &data[1], &data[2]);
939		break;
940	default:
941		ret = -EINVAL;
942		break;
943	}
944	return ret < 0 ? ret : insn->n;
945}
946
947void amplc_dio200_set_enhance(struct comedi_device *dev, unsigned char val)
948{
949	dio200_write8(dev, DIO200_ENHANCE, val);
950}
951EXPORT_SYMBOL_GPL(amplc_dio200_set_enhance);
952
953int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq,
954			       unsigned long req_irq_flags)
955{
956	const struct dio200_board *board = dev->board_ptr;
957	struct comedi_subdevice *s;
958	unsigned int n;
959	int ret;
960
961	ret = comedi_alloc_subdevices(dev, board->n_subdevs);
962	if (ret)
963		return ret;
964
965	for (n = 0; n < dev->n_subdevices; n++) {
966		s = &dev->subdevices[n];
967		switch (board->sdtype[n]) {
968		case sd_8254:
969			/* counter subdevice (8254) */
970			ret = dio200_subdev_8254_init(dev, s,
971						      board->sdinfo[n]);
972			if (ret < 0)
973				return ret;
974			break;
975		case sd_8255:
976			/* digital i/o subdevice (8255) */
977			ret = dio200_subdev_8255_init(dev, s,
978						      board->sdinfo[n]);
979			if (ret < 0)
980				return ret;
981			break;
982		case sd_intr:
983			/* 'INTERRUPT' subdevice */
984			if (irq && !dev->read_subdev) {
985				ret = dio200_subdev_intr_init(dev, s,
986							      DIO200_INT_SCE,
987							      board->sdinfo[n]);
988				if (ret < 0)
989					return ret;
990				dev->read_subdev = s;
991			} else {
992				s->type = COMEDI_SUBD_UNUSED;
993			}
994			break;
995		case sd_timer:
996			s->type		= COMEDI_SUBD_TIMER;
997			s->subdev_flags	= SDF_READABLE | SDF_LSAMPL;
998			s->n_chan	= 1;
999			s->maxdata	= 0xffffffff;
1000			s->insn_read	= dio200_subdev_timer_read;
1001			s->insn_config	= dio200_subdev_timer_config;
1002			break;
1003		default:
1004			s->type = COMEDI_SUBD_UNUSED;
1005			break;
1006		}
1007	}
1008
1009	if (irq && dev->read_subdev) {
1010		if (request_irq(irq, dio200_interrupt, req_irq_flags,
1011				dev->board_name, dev) >= 0) {
1012			dev->irq = irq;
1013		} else {
1014			dev_warn(dev->class_dev,
1015				 "warning! irq %u unavailable!\n", irq);
1016		}
1017	}
1018
1019	return 0;
1020}
1021EXPORT_SYMBOL_GPL(amplc_dio200_common_attach);
1022
1023static int __init amplc_dio200_common_init(void)
1024{
1025	return 0;
1026}
1027module_init(amplc_dio200_common_init);
1028
1029static void __exit amplc_dio200_common_exit(void)
1030{
1031}
1032module_exit(amplc_dio200_common_exit);
1033
1034MODULE_AUTHOR("Comedi http://www.comedi.org");
1035MODULE_DESCRIPTION("Comedi helper for amplc_dio200 and amplc_dio200_pci");
1036MODULE_LICENSE("GPL");
1037