xref: /illumos-gate/usr/src/uts/intel/io/dktp/hba/ghd/ghd_dma.c (revision f304523c)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 #include "ghd.h"
29 
30 void
ghd_dmafree_attr(gcmd_t * gcmdp)31 ghd_dmafree_attr(gcmd_t *gcmdp)
32 {
33 	GDBG_DMA(("ghd_dma_attr_free: gcmdp 0x%p\n", (void *)gcmdp));
34 
35 	if (gcmdp->cmd_dma_handle != NULL) {
36 		if (ddi_dma_unbind_handle(gcmdp->cmd_dma_handle) !=
37 		    DDI_SUCCESS)
38 			cmn_err(CE_WARN, "ghd dma free attr: "
39 			    "unbind handle failed");
40 		ddi_dma_free_handle(&gcmdp->cmd_dma_handle);
41 		GDBG_DMA(("ghd_dma_attr_free: ddi_dma_free 0x%p\n",
42 		    (void *)gcmdp));
43 		gcmdp->cmd_dma_handle = NULL;
44 		gcmdp->cmd_ccount = 0;
45 		gcmdp->cmd_totxfer = 0;
46 	}
47 }
48 
49 
50 int
ghd_dma_buf_bind_attr(ccc_t * cccp,gcmd_t * gcmdp,struct buf * bp,int dma_flags,int (* callback)(),caddr_t arg,ddi_dma_attr_t * sg_attrp)51 ghd_dma_buf_bind_attr(ccc_t		*cccp,
52 			gcmd_t		*gcmdp,
53 			struct buf	*bp,
54 			int		 dma_flags,
55 			int		(*callback)(),
56 			caddr_t		 arg,
57 			ddi_dma_attr_t	*sg_attrp)
58 {
59 	int	 status;
60 
61 	GDBG_DMA(("ghd_dma_attr_get: start: gcmdp 0x%p sg_attrp 0x%p\n",
62 	    (void *)gcmdp, (void *)sg_attrp));
63 
64 
65 	/*
66 	 * First time, need to establish the handle.
67 	 */
68 
69 	ASSERT(gcmdp->cmd_dma_handle == NULL);
70 
71 	status = ddi_dma_alloc_handle(cccp->ccc_hba_dip, sg_attrp, callback,
72 	    arg, &gcmdp->cmd_dma_handle);
73 
74 	if (status != DDI_SUCCESS) {
75 		bp->b_error = 0;
76 		return (FALSE);
77 	}
78 
79 	status = ddi_dma_buf_bind_handle(gcmdp->cmd_dma_handle, bp, dma_flags,
80 	    callback, arg, &gcmdp->cmd_first_cookie, &gcmdp->cmd_ccount);
81 
82 	GDBG_DMA(("ghd_dma_attr_get: setup: gcmdp 0x%p status %d h 0x%p "
83 	    "c 0x%d\n", (void *)gcmdp, status, (void *)gcmdp->cmd_dma_handle,
84 	    gcmdp->cmd_ccount));
85 
86 	switch (status) {
87 	case DDI_DMA_MAPPED:
88 		/* enable first (and only) call to ddi_dma_getwin */
89 		gcmdp->cmd_wcount = 1;
90 		break;
91 
92 	case DDI_DMA_PARTIAL_MAP:
93 		/* enable first call to ddi_dma_getwin */
94 		if (ddi_dma_numwin(gcmdp->cmd_dma_handle, &gcmdp->cmd_wcount) !=
95 		    DDI_SUCCESS) {
96 			bp->b_error = 0;
97 			ddi_dma_free_handle(&gcmdp->cmd_dma_handle);
98 			gcmdp->cmd_dma_handle = NULL;
99 			return (FALSE);
100 		}
101 		break;
102 
103 	case DDI_DMA_NORESOURCES:
104 		bp->b_error = 0;
105 		ddi_dma_free_handle(&gcmdp->cmd_dma_handle);
106 		gcmdp->cmd_dma_handle = NULL;
107 		return (FALSE);
108 
109 	case DDI_DMA_TOOBIG:
110 		bioerror(bp, EINVAL);
111 		ddi_dma_free_handle(&gcmdp->cmd_dma_handle);
112 		gcmdp->cmd_dma_handle = NULL;
113 		return (FALSE);
114 
115 	case DDI_DMA_NOMAPPING:
116 	case DDI_DMA_INUSE:
117 	default:
118 		bioerror(bp, EFAULT);
119 		ddi_dma_free_handle(&gcmdp->cmd_dma_handle);
120 		gcmdp->cmd_dma_handle = NULL;
121 		return (FALSE);
122 	}
123 
124 	/* initialize the loop controls for ghd_dmaget_next_attr() */
125 	gcmdp->cmd_windex = 0;
126 	gcmdp->cmd_cindex = 0;
127 	gcmdp->cmd_totxfer = 0;
128 	gcmdp->cmd_dma_flags = dma_flags;
129 	gcmdp->use_first = 1;
130 	return (TRUE);
131 }
132 
133 
134 uint_t
ghd_dmaget_next_attr(ccc_t * cccp,gcmd_t * gcmdp,long max_transfer_cnt,int sg_size,ddi_dma_cookie_t cookie)135 ghd_dmaget_next_attr(ccc_t *cccp, gcmd_t *gcmdp, long max_transfer_cnt,
136     int sg_size, ddi_dma_cookie_t cookie)
137 {
138 	ulong_t	toxfer = 0;
139 	int	num_segs = 0;
140 	int	single_seg;
141 
142 	GDBG_DMA(("ghd_dma_attr_get: start: gcmdp 0x%p h 0x%p c 0x%x\n",
143 	    (void *)gcmdp, (void *)gcmdp->cmd_dma_handle, gcmdp->cmd_ccount));
144 
145 	/*
146 	 * Disable single-segment Scatter/Gather option
147 	 * if can't do this transfer in a single segment,
148 	 */
149 	if (gcmdp->cmd_cindex + 1 < gcmdp->cmd_ccount) {
150 		single_seg = FALSE;
151 	} else {
152 		single_seg = TRUE;
153 	}
154 
155 
156 	for (;;) {
157 		/*
158 		 * call the controller specific S/G function
159 		 */
160 		(*cccp->ccc_sg_func)(gcmdp, &cookie, single_seg, num_segs);
161 
162 		/* take care of the loop-bookkeeping */
163 		toxfer += cookie.dmac_size;
164 		num_segs++;
165 		gcmdp->cmd_cindex++;
166 
167 		/*
168 		 * if this was the last cookie in the current window
169 		 * set the loop controls start the next window and
170 		 * exit so the HBA can do this partial transfer
171 		 */
172 		if (gcmdp->cmd_cindex >= gcmdp->cmd_ccount) {
173 			gcmdp->cmd_windex++;
174 			gcmdp->cmd_cindex = 0;
175 			break;
176 		}
177 		ASSERT(single_seg == FALSE);
178 
179 		if (toxfer >= max_transfer_cnt)
180 			break;
181 
182 		if (num_segs >= sg_size)
183 			break;
184 
185 		ddi_dma_nextcookie(gcmdp->cmd_dma_handle, &cookie);
186 	}
187 
188 	gcmdp->cmd_totxfer += toxfer;
189 
190 	return ((uint_t)toxfer);
191 }
192 
193 
194 
195 int
ghd_dmaget_attr(ccc_t * cccp,gcmd_t * gcmdp,long count,int sg_size,uint_t * xfer)196 ghd_dmaget_attr(ccc_t		*cccp,
197 		gcmd_t		*gcmdp,
198 		long		count,
199 		int		sg_size,
200 		uint_t		*xfer)
201 {
202 	int	status;
203 	ddi_dma_cookie_t cookie;
204 
205 	*xfer = 0;
206 
207 
208 	if (gcmdp->use_first == 1) {
209 		cookie = gcmdp->cmd_first_cookie;
210 		gcmdp->use_first = 0;
211 	} else if (gcmdp->cmd_windex >= gcmdp->cmd_wcount) {
212 		/*
213 		 * reached the end of buffer. This should not happen.
214 		 */
215 		ASSERT(gcmdp->cmd_windex < gcmdp->cmd_wcount);
216 		return (FALSE);
217 
218 	} else if (gcmdp->cmd_cindex == 0) {
219 		off_t	offset;
220 		size_t	length;
221 
222 		/*
223 		 * start the next window, and get its first cookie
224 		 */
225 		status = ddi_dma_getwin(gcmdp->cmd_dma_handle,
226 		    gcmdp->cmd_windex, &offset, &length,
227 		    &cookie, &gcmdp->cmd_ccount);
228 		if (status != DDI_SUCCESS)
229 			return (FALSE);
230 
231 	} else {
232 		/*
233 		 * get the next cookie in the current window
234 		 */
235 		ddi_dma_nextcookie(gcmdp->cmd_dma_handle, &cookie);
236 	}
237 
238 	/*
239 	 * start the Scatter/Gather loop passing in the first
240 	 * cookie obtained above
241 	 */
242 	*xfer = ghd_dmaget_next_attr(cccp, gcmdp, count, sg_size, cookie);
243 	return (TRUE);
244 }
245