xref: /openbmc/linux/drivers/comedi/drivers/dt2814.c (revision df0e68c1)
18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
28ffdff6aSGreg Kroah-Hartman /*
38ffdff6aSGreg Kroah-Hartman  * comedi/drivers/dt2814.c
48ffdff6aSGreg Kroah-Hartman  * Hardware driver for Data Translation DT2814
58ffdff6aSGreg Kroah-Hartman  *
68ffdff6aSGreg Kroah-Hartman  * COMEDI - Linux Control and Measurement Device Interface
78ffdff6aSGreg Kroah-Hartman  * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
88ffdff6aSGreg Kroah-Hartman  */
98ffdff6aSGreg Kroah-Hartman /*
108ffdff6aSGreg Kroah-Hartman  * Driver: dt2814
118ffdff6aSGreg Kroah-Hartman  * Description: Data Translation DT2814
128ffdff6aSGreg Kroah-Hartman  * Author: ds
138ffdff6aSGreg Kroah-Hartman  * Status: complete
148ffdff6aSGreg Kroah-Hartman  * Devices: [Data Translation] DT2814 (dt2814)
158ffdff6aSGreg Kroah-Hartman  *
168ffdff6aSGreg Kroah-Hartman  * Configuration options:
178ffdff6aSGreg Kroah-Hartman  * [0] - I/O port base address
188ffdff6aSGreg Kroah-Hartman  * [1] - IRQ
198ffdff6aSGreg Kroah-Hartman  *
208ffdff6aSGreg Kroah-Hartman  * This card has 16 analog inputs multiplexed onto a 12 bit ADC.  There
218ffdff6aSGreg Kroah-Hartman  * is a minimally useful onboard clock.  The base frequency for the
228ffdff6aSGreg Kroah-Hartman  * clock is selected by jumpers, and the clock divider can be selected
238ffdff6aSGreg Kroah-Hartman  * via programmed I/O.  Unfortunately, the clock divider can only be
248ffdff6aSGreg Kroah-Hartman  * a power of 10, from 1 to 10^7, of which only 3 or 4 are useful.  In
258ffdff6aSGreg Kroah-Hartman  * addition, the clock does not seem to be very accurate.
268ffdff6aSGreg Kroah-Hartman  */
278ffdff6aSGreg Kroah-Hartman 
288ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
298ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h>
30*df0e68c1SIan Abbott #include <linux/comedi/comedidev.h>
318ffdff6aSGreg Kroah-Hartman #include <linux/delay.h>
328ffdff6aSGreg Kroah-Hartman 
338ffdff6aSGreg Kroah-Hartman #define DT2814_CSR 0
348ffdff6aSGreg Kroah-Hartman #define DT2814_DATA 1
358ffdff6aSGreg Kroah-Hartman 
368ffdff6aSGreg Kroah-Hartman /*
378ffdff6aSGreg Kroah-Hartman  * flags
388ffdff6aSGreg Kroah-Hartman  */
398ffdff6aSGreg Kroah-Hartman 
408ffdff6aSGreg Kroah-Hartman #define DT2814_FINISH 0x80
418ffdff6aSGreg Kroah-Hartman #define DT2814_ERR 0x40
428ffdff6aSGreg Kroah-Hartman #define DT2814_BUSY 0x20
438ffdff6aSGreg Kroah-Hartman #define DT2814_ENB 0x10
448ffdff6aSGreg Kroah-Hartman #define DT2814_CHANMASK 0x0f
458ffdff6aSGreg Kroah-Hartman 
468ffdff6aSGreg Kroah-Hartman #define DT2814_TIMEOUT 10
478ffdff6aSGreg Kroah-Hartman #define DT2814_MAX_SPEED 100000	/* Arbitrary 10 khz limit */
488ffdff6aSGreg Kroah-Hartman 
dt2814_ai_notbusy(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)498ffdff6aSGreg Kroah-Hartman static int dt2814_ai_notbusy(struct comedi_device *dev,
508ffdff6aSGreg Kroah-Hartman 			     struct comedi_subdevice *s,
518ffdff6aSGreg Kroah-Hartman 			     struct comedi_insn *insn,
528ffdff6aSGreg Kroah-Hartman 			     unsigned long context)
538ffdff6aSGreg Kroah-Hartman {
548ffdff6aSGreg Kroah-Hartman 	unsigned int status;
558ffdff6aSGreg Kroah-Hartman 
568ffdff6aSGreg Kroah-Hartman 	status = inb(dev->iobase + DT2814_CSR);
578ffdff6aSGreg Kroah-Hartman 	if (context)
588ffdff6aSGreg Kroah-Hartman 		*(unsigned int *)context = status;
598ffdff6aSGreg Kroah-Hartman 	if (status & DT2814_BUSY)
608ffdff6aSGreg Kroah-Hartman 		return -EBUSY;
618ffdff6aSGreg Kroah-Hartman 	return 0;
628ffdff6aSGreg Kroah-Hartman }
638ffdff6aSGreg Kroah-Hartman 
dt2814_ai_clear(struct comedi_device * dev)648ffdff6aSGreg Kroah-Hartman static int dt2814_ai_clear(struct comedi_device *dev)
658ffdff6aSGreg Kroah-Hartman {
668ffdff6aSGreg Kroah-Hartman 	unsigned int status = 0;
678ffdff6aSGreg Kroah-Hartman 	int ret;
688ffdff6aSGreg Kroah-Hartman 
698ffdff6aSGreg Kroah-Hartman 	/* Wait until not busy and get status register value. */
708ffdff6aSGreg Kroah-Hartman 	ret = comedi_timeout(dev, NULL, NULL, dt2814_ai_notbusy,
718ffdff6aSGreg Kroah-Hartman 			     (unsigned long)&status);
728ffdff6aSGreg Kroah-Hartman 	if (ret)
738ffdff6aSGreg Kroah-Hartman 		return ret;
748ffdff6aSGreg Kroah-Hartman 
758ffdff6aSGreg Kroah-Hartman 	if (status & (DT2814_FINISH | DT2814_ERR)) {
768ffdff6aSGreg Kroah-Hartman 		/*
778ffdff6aSGreg Kroah-Hartman 		 * There unread data, or the error flag is set.
788ffdff6aSGreg Kroah-Hartman 		 * Read the data register twice to clear the condition.
798ffdff6aSGreg Kroah-Hartman 		 */
808ffdff6aSGreg Kroah-Hartman 		inb(dev->iobase + DT2814_DATA);
818ffdff6aSGreg Kroah-Hartman 		inb(dev->iobase + DT2814_DATA);
828ffdff6aSGreg Kroah-Hartman 	}
838ffdff6aSGreg Kroah-Hartman 	return 0;
848ffdff6aSGreg Kroah-Hartman }
858ffdff6aSGreg Kroah-Hartman 
dt2814_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)868ffdff6aSGreg Kroah-Hartman static int dt2814_ai_eoc(struct comedi_device *dev,
878ffdff6aSGreg Kroah-Hartman 			 struct comedi_subdevice *s,
888ffdff6aSGreg Kroah-Hartman 			 struct comedi_insn *insn,
898ffdff6aSGreg Kroah-Hartman 			 unsigned long context)
908ffdff6aSGreg Kroah-Hartman {
918ffdff6aSGreg Kroah-Hartman 	unsigned int status;
928ffdff6aSGreg Kroah-Hartman 
938ffdff6aSGreg Kroah-Hartman 	status = inb(dev->iobase + DT2814_CSR);
948ffdff6aSGreg Kroah-Hartman 	if (status & DT2814_FINISH)
958ffdff6aSGreg Kroah-Hartman 		return 0;
968ffdff6aSGreg Kroah-Hartman 	return -EBUSY;
978ffdff6aSGreg Kroah-Hartman }
988ffdff6aSGreg Kroah-Hartman 
dt2814_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)998ffdff6aSGreg Kroah-Hartman static int dt2814_ai_insn_read(struct comedi_device *dev,
1008ffdff6aSGreg Kroah-Hartman 			       struct comedi_subdevice *s,
1018ffdff6aSGreg Kroah-Hartman 			       struct comedi_insn *insn, unsigned int *data)
1028ffdff6aSGreg Kroah-Hartman {
1038ffdff6aSGreg Kroah-Hartman 	int n, hi, lo;
1048ffdff6aSGreg Kroah-Hartman 	int chan;
1058ffdff6aSGreg Kroah-Hartman 	int ret;
1068ffdff6aSGreg Kroah-Hartman 
1078ffdff6aSGreg Kroah-Hartman 	dt2814_ai_clear(dev);	/* clear stale data or error */
1088ffdff6aSGreg Kroah-Hartman 	for (n = 0; n < insn->n; n++) {
1098ffdff6aSGreg Kroah-Hartman 		chan = CR_CHAN(insn->chanspec);
1108ffdff6aSGreg Kroah-Hartman 
1118ffdff6aSGreg Kroah-Hartman 		outb(chan, dev->iobase + DT2814_CSR);
1128ffdff6aSGreg Kroah-Hartman 
1138ffdff6aSGreg Kroah-Hartman 		ret = comedi_timeout(dev, s, insn, dt2814_ai_eoc, 0);
1148ffdff6aSGreg Kroah-Hartman 		if (ret)
1158ffdff6aSGreg Kroah-Hartman 			return ret;
1168ffdff6aSGreg Kroah-Hartman 
1178ffdff6aSGreg Kroah-Hartman 		hi = inb(dev->iobase + DT2814_DATA);
1188ffdff6aSGreg Kroah-Hartman 		lo = inb(dev->iobase + DT2814_DATA);
1198ffdff6aSGreg Kroah-Hartman 
1208ffdff6aSGreg Kroah-Hartman 		data[n] = (hi << 4) | (lo >> 4);
1218ffdff6aSGreg Kroah-Hartman 	}
1228ffdff6aSGreg Kroah-Hartman 
1238ffdff6aSGreg Kroah-Hartman 	return n;
1248ffdff6aSGreg Kroah-Hartman }
1258ffdff6aSGreg Kroah-Hartman 
dt2814_ns_to_timer(unsigned int * ns,unsigned int flags)1268ffdff6aSGreg Kroah-Hartman static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags)
1278ffdff6aSGreg Kroah-Hartman {
1288ffdff6aSGreg Kroah-Hartman 	int i;
1298ffdff6aSGreg Kroah-Hartman 	unsigned int f;
1308ffdff6aSGreg Kroah-Hartman 
1318ffdff6aSGreg Kroah-Hartman 	/* XXX ignores flags */
1328ffdff6aSGreg Kroah-Hartman 
1338ffdff6aSGreg Kroah-Hartman 	f = 10000;		/* ns */
1348ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < 8; i++) {
1358ffdff6aSGreg Kroah-Hartman 		if ((2 * (*ns)) < (f * 11))
1368ffdff6aSGreg Kroah-Hartman 			break;
1378ffdff6aSGreg Kroah-Hartman 		f *= 10;
1388ffdff6aSGreg Kroah-Hartman 	}
1398ffdff6aSGreg Kroah-Hartman 
1408ffdff6aSGreg Kroah-Hartman 	*ns = f;
1418ffdff6aSGreg Kroah-Hartman 
1428ffdff6aSGreg Kroah-Hartman 	return i;
1438ffdff6aSGreg Kroah-Hartman }
1448ffdff6aSGreg Kroah-Hartman 
dt2814_ai_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)1458ffdff6aSGreg Kroah-Hartman static int dt2814_ai_cmdtest(struct comedi_device *dev,
1468ffdff6aSGreg Kroah-Hartman 			     struct comedi_subdevice *s, struct comedi_cmd *cmd)
1478ffdff6aSGreg Kroah-Hartman {
1488ffdff6aSGreg Kroah-Hartman 	int err = 0;
1498ffdff6aSGreg Kroah-Hartman 	unsigned int arg;
1508ffdff6aSGreg Kroah-Hartman 
1518ffdff6aSGreg Kroah-Hartman 	/* Step 1 : check if triggers are trivially valid */
1528ffdff6aSGreg Kroah-Hartman 
1538ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
1548ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
1558ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
1568ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
1578ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
1588ffdff6aSGreg Kroah-Hartman 
1598ffdff6aSGreg Kroah-Hartman 	if (err)
1608ffdff6aSGreg Kroah-Hartman 		return 1;
1618ffdff6aSGreg Kroah-Hartman 
1628ffdff6aSGreg Kroah-Hartman 	/* Step 2a : make sure trigger sources are unique */
1638ffdff6aSGreg Kroah-Hartman 
1648ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_is_unique(cmd->stop_src);
1658ffdff6aSGreg Kroah-Hartman 
1668ffdff6aSGreg Kroah-Hartman 	/* Step 2b : and mutually compatible */
1678ffdff6aSGreg Kroah-Hartman 
1688ffdff6aSGreg Kroah-Hartman 	if (err)
1698ffdff6aSGreg Kroah-Hartman 		return 2;
1708ffdff6aSGreg Kroah-Hartman 
1718ffdff6aSGreg Kroah-Hartman 	/* Step 3: check if arguments are trivially valid */
1728ffdff6aSGreg Kroah-Hartman 
1738ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
1748ffdff6aSGreg Kroah-Hartman 
1758ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000);
1768ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
1778ffdff6aSGreg Kroah-Hartman 					    DT2814_MAX_SPEED);
1788ffdff6aSGreg Kroah-Hartman 
1798ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
1808ffdff6aSGreg Kroah-Hartman 					   cmd->chanlist_len);
1818ffdff6aSGreg Kroah-Hartman 
1828ffdff6aSGreg Kroah-Hartman 	if (cmd->stop_src == TRIG_COUNT)
1838ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 2);
1848ffdff6aSGreg Kroah-Hartman 	else	/* TRIG_NONE */
1858ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
1868ffdff6aSGreg Kroah-Hartman 
1878ffdff6aSGreg Kroah-Hartman 	if (err)
1888ffdff6aSGreg Kroah-Hartman 		return 3;
1898ffdff6aSGreg Kroah-Hartman 
1908ffdff6aSGreg Kroah-Hartman 	/* step 4: fix up any arguments */
1918ffdff6aSGreg Kroah-Hartman 
1928ffdff6aSGreg Kroah-Hartman 	arg = cmd->scan_begin_arg;
1938ffdff6aSGreg Kroah-Hartman 	dt2814_ns_to_timer(&arg, cmd->flags);
1948ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
1958ffdff6aSGreg Kroah-Hartman 
1968ffdff6aSGreg Kroah-Hartman 	if (err)
1978ffdff6aSGreg Kroah-Hartman 		return 4;
1988ffdff6aSGreg Kroah-Hartman 
1998ffdff6aSGreg Kroah-Hartman 	return 0;
2008ffdff6aSGreg Kroah-Hartman }
2018ffdff6aSGreg Kroah-Hartman 
dt2814_ai_cmd(struct comedi_device * dev,struct comedi_subdevice * s)2028ffdff6aSGreg Kroah-Hartman static int dt2814_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
2038ffdff6aSGreg Kroah-Hartman {
2048ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &s->async->cmd;
2058ffdff6aSGreg Kroah-Hartman 	int chan;
2068ffdff6aSGreg Kroah-Hartman 	int trigvar;
2078ffdff6aSGreg Kroah-Hartman 
2088ffdff6aSGreg Kroah-Hartman 	dt2814_ai_clear(dev);	/* clear stale data or error */
2098ffdff6aSGreg Kroah-Hartman 	trigvar = dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags);
2108ffdff6aSGreg Kroah-Hartman 
2118ffdff6aSGreg Kroah-Hartman 	chan = CR_CHAN(cmd->chanlist[0]);
2128ffdff6aSGreg Kroah-Hartman 
2138ffdff6aSGreg Kroah-Hartman 	outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR);
2148ffdff6aSGreg Kroah-Hartman 
2158ffdff6aSGreg Kroah-Hartman 	return 0;
2168ffdff6aSGreg Kroah-Hartman }
2178ffdff6aSGreg Kroah-Hartman 
dt2814_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)2188ffdff6aSGreg Kroah-Hartman static int dt2814_ai_cancel(struct comedi_device *dev,
2198ffdff6aSGreg Kroah-Hartman 			    struct comedi_subdevice *s)
2208ffdff6aSGreg Kroah-Hartman {
2218ffdff6aSGreg Kroah-Hartman 	unsigned int status;
2228ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
2238ffdff6aSGreg Kroah-Hartman 
2248ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&dev->spinlock, flags);
2258ffdff6aSGreg Kroah-Hartman 	status = inb(dev->iobase + DT2814_CSR);
2268ffdff6aSGreg Kroah-Hartman 	if (status & DT2814_ENB) {
2278ffdff6aSGreg Kroah-Hartman 		/*
2288ffdff6aSGreg Kroah-Hartman 		 * Clear the timed trigger enable bit.
2298ffdff6aSGreg Kroah-Hartman 		 *
2308ffdff6aSGreg Kroah-Hartman 		 * Note: turning off timed mode triggers another
2318ffdff6aSGreg Kroah-Hartman 		 * sample.  This will be mopped up by the calls to
2328ffdff6aSGreg Kroah-Hartman 		 * dt2814_ai_clear().
2338ffdff6aSGreg Kroah-Hartman 		 */
2348ffdff6aSGreg Kroah-Hartman 		outb(status & DT2814_CHANMASK, dev->iobase + DT2814_CSR);
2358ffdff6aSGreg Kroah-Hartman 	}
2368ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&dev->spinlock, flags);
2378ffdff6aSGreg Kroah-Hartman 	return 0;
2388ffdff6aSGreg Kroah-Hartman }
2398ffdff6aSGreg Kroah-Hartman 
dt2814_interrupt(int irq,void * d)2408ffdff6aSGreg Kroah-Hartman static irqreturn_t dt2814_interrupt(int irq, void *d)
2418ffdff6aSGreg Kroah-Hartman {
2428ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = d;
2438ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s = dev->read_subdev;
2448ffdff6aSGreg Kroah-Hartman 	struct comedi_async *async;
2458ffdff6aSGreg Kroah-Hartman 	unsigned int lo, hi;
2468ffdff6aSGreg Kroah-Hartman 	unsigned short data;
2478ffdff6aSGreg Kroah-Hartman 	unsigned int status;
2488ffdff6aSGreg Kroah-Hartman 
2498ffdff6aSGreg Kroah-Hartman 	if (!dev->attached) {
2508ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev, "spurious interrupt\n");
2518ffdff6aSGreg Kroah-Hartman 		return IRQ_HANDLED;
2528ffdff6aSGreg Kroah-Hartman 	}
2538ffdff6aSGreg Kroah-Hartman 
2548ffdff6aSGreg Kroah-Hartman 	async = s->async;
2558ffdff6aSGreg Kroah-Hartman 
2568ffdff6aSGreg Kroah-Hartman 	spin_lock(&dev->spinlock);
2578ffdff6aSGreg Kroah-Hartman 
2588ffdff6aSGreg Kroah-Hartman 	status = inb(dev->iobase + DT2814_CSR);
2598ffdff6aSGreg Kroah-Hartman 	if (!(status & DT2814_ENB)) {
2608ffdff6aSGreg Kroah-Hartman 		/* Timed acquisition not enabled.  Nothing to do. */
2618ffdff6aSGreg Kroah-Hartman 		spin_unlock(&dev->spinlock);
2628ffdff6aSGreg Kroah-Hartman 		return IRQ_HANDLED;
2638ffdff6aSGreg Kroah-Hartman 	}
2648ffdff6aSGreg Kroah-Hartman 
2658ffdff6aSGreg Kroah-Hartman 	if (!(status & (DT2814_FINISH | DT2814_ERR))) {
2668ffdff6aSGreg Kroah-Hartman 		/* Spurious interrupt? */
2678ffdff6aSGreg Kroah-Hartman 		spin_unlock(&dev->spinlock);
2688ffdff6aSGreg Kroah-Hartman 		return IRQ_HANDLED;
2698ffdff6aSGreg Kroah-Hartman 	}
2708ffdff6aSGreg Kroah-Hartman 
2718ffdff6aSGreg Kroah-Hartman 	/* Read data or clear error. */
2728ffdff6aSGreg Kroah-Hartman 	hi = inb(dev->iobase + DT2814_DATA);
2738ffdff6aSGreg Kroah-Hartman 	lo = inb(dev->iobase + DT2814_DATA);
2748ffdff6aSGreg Kroah-Hartman 
2758ffdff6aSGreg Kroah-Hartman 	data = (hi << 4) | (lo >> 4);
2768ffdff6aSGreg Kroah-Hartman 
2778ffdff6aSGreg Kroah-Hartman 	if (status & DT2814_ERR) {
2788ffdff6aSGreg Kroah-Hartman 		async->events |= COMEDI_CB_ERROR;
2798ffdff6aSGreg Kroah-Hartman 	} else {
2808ffdff6aSGreg Kroah-Hartman 		comedi_buf_write_samples(s, &data, 1);
2818ffdff6aSGreg Kroah-Hartman 		if (async->cmd.stop_src == TRIG_COUNT &&
2828ffdff6aSGreg Kroah-Hartman 		    async->scans_done >=  async->cmd.stop_arg) {
2838ffdff6aSGreg Kroah-Hartman 			async->events |= COMEDI_CB_EOA;
2848ffdff6aSGreg Kroah-Hartman 		}
2858ffdff6aSGreg Kroah-Hartman 	}
2868ffdff6aSGreg Kroah-Hartman 	if (async->events & COMEDI_CB_CANCEL_MASK) {
2878ffdff6aSGreg Kroah-Hartman 		/*
2888ffdff6aSGreg Kroah-Hartman 		 * Disable timed mode.
2898ffdff6aSGreg Kroah-Hartman 		 *
2908ffdff6aSGreg Kroah-Hartman 		 * Note: turning off timed mode triggers another
2918ffdff6aSGreg Kroah-Hartman 		 * sample.  This will be mopped up by the calls to
2928ffdff6aSGreg Kroah-Hartman 		 * dt2814_ai_clear().
2938ffdff6aSGreg Kroah-Hartman 		 */
2948ffdff6aSGreg Kroah-Hartman 		outb(status & DT2814_CHANMASK, dev->iobase + DT2814_CSR);
2958ffdff6aSGreg Kroah-Hartman 	}
2968ffdff6aSGreg Kroah-Hartman 
2978ffdff6aSGreg Kroah-Hartman 	spin_unlock(&dev->spinlock);
2988ffdff6aSGreg Kroah-Hartman 
2998ffdff6aSGreg Kroah-Hartman 	comedi_handle_events(dev, s);
3008ffdff6aSGreg Kroah-Hartman 	return IRQ_HANDLED;
3018ffdff6aSGreg Kroah-Hartman }
3028ffdff6aSGreg Kroah-Hartman 
dt2814_attach(struct comedi_device * dev,struct comedi_devconfig * it)3038ffdff6aSGreg Kroah-Hartman static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it)
3048ffdff6aSGreg Kroah-Hartman {
3058ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s;
3068ffdff6aSGreg Kroah-Hartman 	int ret;
3078ffdff6aSGreg Kroah-Hartman 
3088ffdff6aSGreg Kroah-Hartman 	ret = comedi_request_region(dev, it->options[0], 0x2);
3098ffdff6aSGreg Kroah-Hartman 	if (ret)
3108ffdff6aSGreg Kroah-Hartman 		return ret;
3118ffdff6aSGreg Kroah-Hartman 
3128ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + DT2814_CSR);
3138ffdff6aSGreg Kroah-Hartman 	if (dt2814_ai_clear(dev)) {
3148ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev, "reset error (fatal)\n");
3158ffdff6aSGreg Kroah-Hartman 		return -EIO;
3168ffdff6aSGreg Kroah-Hartman 	}
3178ffdff6aSGreg Kroah-Hartman 
3188ffdff6aSGreg Kroah-Hartman 	if (it->options[1]) {
3198ffdff6aSGreg Kroah-Hartman 		ret = request_irq(it->options[1], dt2814_interrupt, 0,
3208ffdff6aSGreg Kroah-Hartman 				  dev->board_name, dev);
3218ffdff6aSGreg Kroah-Hartman 		if (ret == 0)
3228ffdff6aSGreg Kroah-Hartman 			dev->irq = it->options[1];
3238ffdff6aSGreg Kroah-Hartman 	}
3248ffdff6aSGreg Kroah-Hartman 
3258ffdff6aSGreg Kroah-Hartman 	ret = comedi_alloc_subdevices(dev, 1);
3268ffdff6aSGreg Kroah-Hartman 	if (ret)
3278ffdff6aSGreg Kroah-Hartman 		return ret;
3288ffdff6aSGreg Kroah-Hartman 
3298ffdff6aSGreg Kroah-Hartman 	s = &dev->subdevices[0];
3308ffdff6aSGreg Kroah-Hartman 	s->type = COMEDI_SUBD_AI;
3318ffdff6aSGreg Kroah-Hartman 	s->subdev_flags = SDF_READABLE | SDF_GROUND;
3328ffdff6aSGreg Kroah-Hartman 	s->n_chan = 16;		/* XXX */
3338ffdff6aSGreg Kroah-Hartman 	s->insn_read = dt2814_ai_insn_read;
3348ffdff6aSGreg Kroah-Hartman 	s->maxdata = 0xfff;
3358ffdff6aSGreg Kroah-Hartman 	s->range_table = &range_unknown;	/* XXX */
3368ffdff6aSGreg Kroah-Hartman 	if (dev->irq) {
3378ffdff6aSGreg Kroah-Hartman 		dev->read_subdev = s;
3388ffdff6aSGreg Kroah-Hartman 		s->subdev_flags |= SDF_CMD_READ;
3398ffdff6aSGreg Kroah-Hartman 		s->len_chanlist = 1;
3408ffdff6aSGreg Kroah-Hartman 		s->do_cmd = dt2814_ai_cmd;
3418ffdff6aSGreg Kroah-Hartman 		s->do_cmdtest = dt2814_ai_cmdtest;
3428ffdff6aSGreg Kroah-Hartman 		s->cancel = dt2814_ai_cancel;
3438ffdff6aSGreg Kroah-Hartman 	}
3448ffdff6aSGreg Kroah-Hartman 
3458ffdff6aSGreg Kroah-Hartman 	return 0;
3468ffdff6aSGreg Kroah-Hartman }
3478ffdff6aSGreg Kroah-Hartman 
dt2814_detach(struct comedi_device * dev)3488ffdff6aSGreg Kroah-Hartman static void dt2814_detach(struct comedi_device *dev)
3498ffdff6aSGreg Kroah-Hartman {
3508ffdff6aSGreg Kroah-Hartman 	if (dev->irq) {
3518ffdff6aSGreg Kroah-Hartman 		/*
3528ffdff6aSGreg Kroah-Hartman 		 * An extra conversion triggered on termination of an
3538ffdff6aSGreg Kroah-Hartman 		 * asynchronous command may still be in progress.  Wait for
3548ffdff6aSGreg Kroah-Hartman 		 * it to finish and clear the data or error status.
3558ffdff6aSGreg Kroah-Hartman 		 */
3568ffdff6aSGreg Kroah-Hartman 		dt2814_ai_clear(dev);
3578ffdff6aSGreg Kroah-Hartman 	}
3588ffdff6aSGreg Kroah-Hartman 	comedi_legacy_detach(dev);
3598ffdff6aSGreg Kroah-Hartman }
3608ffdff6aSGreg Kroah-Hartman 
3618ffdff6aSGreg Kroah-Hartman static struct comedi_driver dt2814_driver = {
3628ffdff6aSGreg Kroah-Hartman 	.driver_name	= "dt2814",
3638ffdff6aSGreg Kroah-Hartman 	.module		= THIS_MODULE,
3648ffdff6aSGreg Kroah-Hartman 	.attach		= dt2814_attach,
3658ffdff6aSGreg Kroah-Hartman 	.detach		= dt2814_detach,
3668ffdff6aSGreg Kroah-Hartman };
3678ffdff6aSGreg Kroah-Hartman module_comedi_driver(dt2814_driver);
3688ffdff6aSGreg Kroah-Hartman 
3698ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org");
3708ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi low-level driver");
3718ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
372