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