xref: /illumos-gate/usr/src/uts/common/io/dma_engine.c (revision 7a6d80f1660abd4755c68cbd094d4a914681d26e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 /*
32  * This is the implementation of the kernel DMA interface for the
33  * AT Class machines using Intel 8237A DMAC.
34  *
35  * The following routines in the interface are implemented:
36  *	i_dmae_init()
37  *	_dmae_nxcookie()
38  *	i_dmae_acquire()
39  *	i_dmae_free()
40  *	i_dmae_prog()
41  *	i_dmae_swsetup()
42  *	i_dmae_swstart()
43  *	i_dmae_stop()
44  *	i_dmae_enable()
45  *	i_dmae_disable()
46  *	i_dmae_get_best_mode()
47  *	i_dmae_get_chan_stat()
48  *
49  */
50 
51 #include <sys/types.h>
52 #include <sys/param.h>
53 #include <sys/buf.h>
54 #include <sys/kmem.h>
55 #include <sys/sunddi.h>
56 #include <sys/cmn_err.h>
57 #include <sys/dma_engine.h>
58 #include <sys/dma_i8237A.h>
59 
60 #ifdef DEBUG
61 #include <sys/promif.h>
62 static int dmaedebug = 0;
63 #define	dprintf(x)	if (dmaedebug) prom_printf x
64 #else
65 #define	dprintf(x)
66 #endif
67 
68 
69 static struct dmae_chnl dmae_stat[NCHANS];
70 static uintptr_t dmae_call_list[NCHANS] = {0, 0, 0, 0, 0, 0, 0, 0};
71 
72 /*
73  *  routine: i_dmae_init()
74  *  purpose: called to initialize the dma interface, the DMAC, and any
75  *           dma data structures. Called during system initialization.
76  *  caller:  main()
77  *  calls:   d37A_init()
78  */
79 
80 int
81 i_dmae_init(dev_info_t *dip)
82 {
83 	int chnl;
84 
85 	dprintf(("i_dmae_init: initializing dma.\n"));
86 
87 	/* initialize semaphore map */
88 	for (chnl = 0; chnl < NCHANS; chnl++) {
89 		sema_init(&dmae_stat[chnl].dch_lock, 1, NULL, SEMA_DRIVER,
90 		    (void *)NULL);
91 	}
92 	return (d37A_init(dip));
93 }
94 
95 
96 /*
97  *  routine: i_dmae_acquire()
98  *  purpose: Request the semaphore for the indicated channel.
99  *           A call_back function can be passed if caller does/cannot
100  *           wait for the semaphore.
101  *  caller:  drivers
102  *  calls:   sema_p(), sema_tryp(), ddi_set_callback()
103  */
104 int
105 i_dmae_acquire(dev_info_t *dip, int chnl, int (*dmae_waitfp)(), caddr_t arg)
106 {
107 #if defined(lint)
108 	dip = dip;
109 #endif
110 	dprintf(("i_dmae_acquire: channel %d, waitfp %p\n",
111 	    chnl, (void *)dmae_waitfp));
112 
113 	if (!d37A_dma_valid(chnl))
114 		return (DDI_FAILURE);
115 
116 	if (dmae_waitfp == DDI_DMA_SLEEP) {
117 		sema_p(&dmae_stat[chnl].dch_lock);
118 	} else if (sema_tryp(&dmae_stat[chnl].dch_lock) == 0) {
119 		if (dmae_waitfp == DDI_DMA_DONTWAIT) {
120 			dprintf(("_dma_acquire: channel %d is busy.\n", chnl));
121 		} else {
122 			ddi_set_callback(dmae_waitfp, arg,
123 			    &dmae_call_list[chnl]);
124 		}
125 		return (DDI_DMA_NORESOURCES);
126 	}
127 
128 	/*
129 	 * XXX -  save dip for authentication later ??
130 	 */
131 	dprintf(("_dma_acquire: channel %d now allocated.\n", chnl));
132 	return (DDI_SUCCESS);
133 }
134 
135 
136 /*
137  *  routine: i_dmae_free()
138  *  purpose: Release the channel semaphore on chnl. Assumes caller actually
139  *           owns the semaphore (no check made for this).
140  *  caller:  drivers
141  *  calls:   none
142  */
143 
144 int
145 i_dmae_free(dev_info_t *dip, int chnl)
146 {
147 #if defined(lint)
148 	dip = dip;
149 #endif
150 	dprintf(("i_dmae_free: channel %d\n", chnl));
151 
152 	d37A_dma_release(chnl);
153 	/*
154 	 * XXX - should dip be authenticated as the one that did acquire?
155 	 */
156 	sema_v(&dmae_stat[chnl].dch_lock);
157 
158 	if (dmae_call_list[chnl])
159 		ddi_run_callback(&dmae_call_list[chnl]);
160 	return (DDI_SUCCESS);
161 }
162 
163 /*
164  *  routine: i_dmae_get_best_mode()
165  *  purpose: confirm that data is aligned for efficient flyby mode
166  *  caller:  driver routines.
167  *  calls:   d37A_get_best_mode.
168  */
169 
170 uchar_t
171 i_dmae_get_best_mode(dev_info_t *dip, struct ddi_dmae_req *dmaereqp)
172 {
173 #if defined(lint)
174 	dip = dip;
175 #endif
176 	return (d37A_get_best_mode(dmaereqp));
177 }
178 
179 /*
180  *  routine: _dmae_nxcookie()
181  *  purpose: service the interrupt by calling device driver routine for next
182  *		DMA cookie.
183  *  caller:  d37A_intr()
184  *  calls:   routine provided in request structure
185  */
186 
187 ddi_dma_cookie_t *
188 _dmae_nxcookie(int chnl)
189 {
190 	ddi_dma_cookie_t *cookiep = NULL;
191 
192 	dprintf(("_dmae_nxcookie: chnl %d\n", chnl));
193 
194 	if (dmae_stat[chnl].proc) {
195 
196 		cookiep = dmae_stat[chnl].proc(dmae_stat[chnl].procparms);
197 		/*
198 		 * expect a cookie pointer from user's routine;
199 		 * null cookie pointer will terminate chaining
200 		 */
201 	}
202 	return (cookiep);
203 }
204 
205 
206 /*
207  *  routine: i_dmae_prog()
208  *  purpose: Program channel for the to_be_initiated_by_hardware operation.
209  *           _dma_acquire is called to request the channel semaphore and
210  *	     mode is passed as the sleep parameter.
211  *	     The channel is enabled after it is setup.
212  *	     Note that the ddi_dmae_req pointer can be to NULL if the mode
213  *	     registers have already been setup by a prior call; this implements
214  *	     a prog_next() to update the address and count registers.
215  *  caller:  driver routines
216  *  calls:   d37A_prog_chan()
217  */
218 
219 int
220 i_dmae_prog(dev_info_t *dip, struct ddi_dmae_req *dmaereqp,
221     ddi_dma_cookie_t *cp, int chnl)
222 {
223 	struct dmae_chnl *dcp;
224 	int rval;
225 
226 #if defined(lint)
227 	dip = dip;
228 #endif
229 	rval = d37A_prog_chan(dmaereqp, cp, chnl);
230 	if (rval != DDI_SUCCESS) {
231 		dprintf(("i_dmae_prog: failure on channel %d dmaereq=%p\n",
232 		    chnl, (void *)dmaereqp));
233 	} else {
234 		dprintf(("i_dmae_prog: channel %d dmaereq=%p\n",
235 		    chnl, (void *)dmaereqp));
236 		dcp = &dmae_stat[chnl];
237 		dcp->dch_cookiep = cp;
238 		if (dmaereqp) {
239 			dcp->proc = dmaereqp->proc;
240 			dcp->procparms = dmaereqp->procparms;
241 		}
242 		d37A_dma_enable(chnl);
243 	}
244 	return (rval);
245 }
246 
247 
248 /*
249  *  routine: i_dmae_swsetup()
250  *  purpose: Setup chan for the operation given in dmacbptr.
251  *           _dma_acquire is first called
252  *           to request the channel semaphore for chnl; mode is
253  *           passed to _dma_acquire().
254  *  caller:  driver routines
255  *  calls:   d37A_dma_swsetup()
256  */
257 
258 int
259 i_dmae_swsetup(dev_info_t *dip, struct ddi_dmae_req *dmaereqp,
260     ddi_dma_cookie_t *cp, int chnl)
261 {
262 	struct dmae_chnl *dcp;
263 	int rval;
264 
265 #if defined(lint)
266 	dip = dip;
267 #endif
268 	rval = d37A_dma_swsetup(dmaereqp, cp, chnl);
269 	if (rval != DDI_SUCCESS) {
270 		dprintf(("i_dmae_swsetup: failure on channel %d dmaereq=%p\n",
271 		    chnl, (void *)dmaereqp));
272 	} else {
273 		dprintf(("i_dmae_swsetup: channel %d: dmaereq=%p\n",
274 		    chnl, (void *)dmaereqp));
275 		dcp = &dmae_stat[chnl];
276 		dcp->dch_cookiep = cp;
277 		dcp->proc = dmaereqp->proc;
278 		dcp->procparms = dmaereqp->procparms;
279 	}
280 	return (rval);
281 }
282 
283 
284 /*
285  *  routine: i_dmae_swstart()
286  *  purpose: Start the operation setup by i_dmae_swsetup().
287  *  caller:  driver routines
288  *  calls:   d37A_dma_swstart().
289  */
290 
291 void
292 i_dmae_swstart(dev_info_t *dip, int chnl)
293 {
294 #if defined(lint)
295 	dip = dip;
296 #endif
297 	dprintf(("i_dmae_swstart: channel %d.\n", chnl));
298 
299 	d37A_dma_swstart(chnl);
300 }
301 
302 
303 /*
304  *  routine: i_dmae_stop()
305  *  purpose: stop DMA activity on chnl.
306  *  caller:  driver routines
307  *  calls:   splhi(), _dma_relse(), splx(),
308  *           d37A_dma_stop().
309  */
310 
311 void
312 i_dmae_stop(dev_info_t *dip, int chnl)
313 {
314 #if defined(lint)
315 	dip = dip;
316 #endif
317 	dprintf(("i_dmae_stop: channel %d\n", chnl));
318 
319 	/* call d37A the stop the channel */
320 	d37A_dma_stop(chnl);
321 
322 	dmae_stat[chnl].dch_cookiep = NULL;
323 }
324 
325 
326 /*
327  *  routine: i_dmae_enable()
328  *  purpose: Allow the hardware tied to channel chnl to request service
329  *           from the DMAC. i_dmae_prog() should have been called prior
330  *           to this.
331  *  caller:  driver routines.
332  *  calls:   d37A_dma_enable()
333  */
334 
335 void
336 i_dmae_enable(dev_info_t *dip, int chnl)
337 {
338 #if defined(lint)
339 	dip = dip;
340 #endif
341 	dprintf(("i_dmae_enable: channel %d\n", chnl));
342 
343 	d37A_dma_enable(chnl);
344 }
345 
346 
347 /*
348  *  routine: i_dmae_disable()
349  *  purpose: Called to mask off hardware requests on channel chnl. Assumes
350  *           the caller owns the channel.
351  *  caller:  driver routines.
352  *  calls:   d37A_dma_disable()
353  */
354 
355 void
356 i_dmae_disable(dev_info_t *dip, int chnl)
357 {
358 #if defined(lint)
359 	dip = dip;
360 #endif
361 	/* dprintf(("i_dmae_disable: disable channel %d.\n", chnl)); */
362 
363 	d37A_dma_disable(chnl);
364 
365 	dmae_stat[chnl].dch_cookiep = NULL;
366 }
367 
368 
369 /*
370  *  routine: i_dmae_get_chan_stat()
371  *  purpose: Obtain the current channel status from the DMAC
372  *  caller:  driver routines.
373  *  calls:   d37A_get_chan_stat()
374  */
375 
376 void
377 i_dmae_get_chan_stat(dev_info_t *dip, int chnl, ulong_t *addressp, int *countp)
378 {
379 #if defined(lint)
380 	dip = dip;
381 #endif
382 	dprintf(("i_dmae_get_chan_stat: channel %d", chnl));
383 
384 	d37A_get_chan_stat(chnl, addressp, countp);
385 }
386