xref: /linux/drivers/comedi/drivers/ni_labpc_isadma.c (revision b83deaa741558babf4b8d51d34f6637ccfff1b26)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * comedi/drivers/ni_labpc_isadma.c
4  * ISA DMA support for National Instruments Lab-PC series boards and
5  * compatibles.
6  *
7  * Extracted from ni_labpc.c:
8  * Copyright (C) 2001-2003 Frank Mori Hess <fmhess@users.sourceforge.net>
9  */
10 
11 #include <linux/module.h>
12 #include <linux/slab.h>
13 #include <linux/comedi/comedidev.h>
14 #include <linux/comedi/comedi_isadma.h>
15 
16 #include "ni_labpc.h"
17 #include "ni_labpc_regs.h"
18 #include "ni_labpc_isadma.h"
19 
20 /* size in bytes of dma buffer */
21 #define LABPC_ISADMA_BUFFER_SIZE	0xff00
22 
23 /* utility function that suggests a dma transfer size in bytes */
24 static unsigned int labpc_suggest_transfer_size(struct comedi_device *dev,
25 						struct comedi_subdevice *s,
26 						unsigned int maxbytes)
27 {
28 	struct comedi_cmd *cmd = &s->async->cmd;
29 	unsigned int sample_size = comedi_bytes_per_sample(s);
30 	unsigned int size;
31 	unsigned int freq;
32 
33 	if (cmd->convert_src == TRIG_TIMER)
34 		freq = 1000000000 / cmd->convert_arg;
35 	else
36 		/* return some default value */
37 		freq = 0xffffffff;
38 
39 	/* make buffer fill in no more than 1/3 second */
40 	size = (freq / 3) * sample_size;
41 
42 	/* set a minimum and maximum size allowed */
43 	if (size > maxbytes)
44 		size = maxbytes;
45 	else if (size < sample_size)
46 		size = sample_size;
47 
48 	return size;
49 }
50 
51 void labpc_setup_dma(struct comedi_device *dev, struct comedi_subdevice *s)
52 {
53 	struct labpc_private *devpriv = dev->private;
54 	struct comedi_isadma_desc *desc = &devpriv->dma->desc[0];
55 	struct comedi_cmd *cmd = &s->async->cmd;
56 	unsigned int sample_size = comedi_bytes_per_sample(s);
57 
58 	/* set appropriate size of transfer */
59 	desc->size = labpc_suggest_transfer_size(dev, s, desc->maxsize);
60 	if (cmd->stop_src == TRIG_COUNT &&
61 	    devpriv->count * sample_size < desc->size)
62 		desc->size = devpriv->count * sample_size;
63 
64 	comedi_isadma_program(desc);
65 
66 	/* set CMD3 bits for caller to enable DMA and interrupt */
67 	devpriv->cmd3 |= (CMD3_DMAEN | CMD3_DMATCINTEN);
68 }
69 EXPORT_SYMBOL_GPL(labpc_setup_dma);
70 
71 void labpc_drain_dma(struct comedi_device *dev)
72 {
73 	struct labpc_private *devpriv = dev->private;
74 	struct comedi_isadma_desc *desc = &devpriv->dma->desc[0];
75 	struct comedi_subdevice *s = dev->read_subdev;
76 	struct comedi_async *async = s->async;
77 	struct comedi_cmd *cmd = &async->cmd;
78 	unsigned int max_samples = comedi_bytes_to_samples(s, desc->size);
79 	unsigned int residue;
80 	unsigned int nsamples;
81 	unsigned int leftover;
82 
83 	/*
84 	 * residue is the number of bytes left to be done on the dma
85 	 * transfer.  It should always be zero at this point unless
86 	 * the stop_src is set to external triggering.
87 	 */
88 	residue = comedi_isadma_disable(desc->chan);
89 
90 	/*
91 	 * Figure out how many samples to read for this transfer and
92 	 * how many will be stored for next time.
93 	 */
94 	nsamples = max_samples - comedi_bytes_to_samples(s, residue);
95 	if (cmd->stop_src == TRIG_COUNT) {
96 		if (devpriv->count <= nsamples) {
97 			nsamples = devpriv->count;
98 			leftover = 0;
99 		} else {
100 			leftover = devpriv->count - nsamples;
101 			if (leftover > max_samples)
102 				leftover = max_samples;
103 		}
104 		devpriv->count -= nsamples;
105 	} else {
106 		leftover = max_samples;
107 	}
108 	desc->size = comedi_samples_to_bytes(s, leftover);
109 
110 	comedi_buf_write_samples(s, desc->virt_addr, nsamples);
111 }
112 EXPORT_SYMBOL_GPL(labpc_drain_dma);
113 
114 static void handle_isa_dma(struct comedi_device *dev)
115 {
116 	struct labpc_private *devpriv = dev->private;
117 	struct comedi_isadma_desc *desc = &devpriv->dma->desc[0];
118 
119 	labpc_drain_dma(dev);
120 
121 	if (desc->size)
122 		comedi_isadma_program(desc);
123 
124 	/* clear dma tc interrupt */
125 	devpriv->write_byte(dev, 0x1, DMATC_CLEAR_REG);
126 }
127 
128 void labpc_handle_dma_status(struct comedi_device *dev)
129 {
130 	const struct labpc_boardinfo *board = dev->board_ptr;
131 	struct labpc_private *devpriv = dev->private;
132 
133 	/*
134 	 * if a dma terminal count of external stop trigger
135 	 * has occurred
136 	 */
137 	if (devpriv->stat1 & STAT1_GATA0 ||
138 	    (board->is_labpc1200 && devpriv->stat2 & STAT2_OUTA1))
139 		handle_isa_dma(dev);
140 }
141 EXPORT_SYMBOL_GPL(labpc_handle_dma_status);
142 
143 void labpc_init_dma_chan(struct comedi_device *dev, unsigned int dma_chan)
144 {
145 	struct labpc_private *devpriv = dev->private;
146 
147 	/* only DMA channels 3 and 1 are valid */
148 	if (dma_chan != 1 && dma_chan != 3)
149 		return;
150 
151 	/* DMA uses 1 buffer */
152 	devpriv->dma = comedi_isadma_alloc(dev, 1, dma_chan, dma_chan,
153 					   LABPC_ISADMA_BUFFER_SIZE,
154 					   COMEDI_ISADMA_READ);
155 }
156 EXPORT_SYMBOL_GPL(labpc_init_dma_chan);
157 
158 void labpc_free_dma_chan(struct comedi_device *dev)
159 {
160 	struct labpc_private *devpriv = dev->private;
161 
162 	if (devpriv)
163 		comedi_isadma_free(devpriv->dma);
164 }
165 EXPORT_SYMBOL_GPL(labpc_free_dma_chan);
166 
167 static int __init ni_labpc_isadma_init_module(void)
168 {
169 	return 0;
170 }
171 module_init(ni_labpc_isadma_init_module);
172 
173 static void __exit ni_labpc_isadma_cleanup_module(void)
174 {
175 }
176 module_exit(ni_labpc_isadma_cleanup_module);
177 
178 MODULE_AUTHOR("Comedi https://www.comedi.org");
179 MODULE_DESCRIPTION("Comedi NI Lab-PC ISA DMA support");
180 MODULE_LICENSE("GPL");
181