xref: /illumos-gate/usr/src/uts/intel/io/dktp/hba/ghd/ghd_dma.c (revision 09295472)
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 2005 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 #include "ghd.h"
30 
31 void
32 ghd_dmafree_attr(gcmd_t *gcmdp)
33 {
34 	GDBG_DMA(("ghd_dma_attr_free: gcmdp 0x%p\n", gcmdp));
35 
36 	if (gcmdp->cmd_dma_handle != NULL) {
37 		if (ddi_dma_unbind_handle(gcmdp->cmd_dma_handle) !=
38 		    DDI_SUCCESS)
39 			cmn_err(CE_WARN, "ghd dma free attr: "
40 			    "unbind handle failed");
41 		ddi_dma_free_handle(&gcmdp->cmd_dma_handle);
42 		GDBG_DMA(("ghd_dma_attr_free: ddi_dma_free 0x%p\n", gcmdp));
43 		gcmdp->cmd_dma_handle = NULL;
44 		gcmdp->cmd_ccount = 0;
45 		gcmdp->cmd_totxfer = 0;
46 	}
47 }
48 
49 
50 int
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 		gcmdp, 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,
81 		    &gcmdp->cmd_ccount);
82 
83 	GDBG_DMA(("ghd_dma_attr_get: setup: gcmdp 0x%p status %d h 0x%p "
84 		"c 0x%d\n", gcmdp, status, gcmdp->cmd_dma_handle,
85 			gcmdp->cmd_ccount));
86 
87 	switch (status) {
88 	case DDI_DMA_MAPPED:
89 		/* enable first (and only) call to ddi_dma_getwin */
90 		gcmdp->cmd_wcount = 1;
91 		break;
92 
93 	case DDI_DMA_PARTIAL_MAP:
94 		/* enable first call to ddi_dma_getwin */
95 		if (ddi_dma_numwin(gcmdp->cmd_dma_handle, &gcmdp->cmd_wcount) !=
96 								DDI_SUCCESS) {
97 			bp->b_error = 0;
98 			ddi_dma_free_handle(&gcmdp->cmd_dma_handle);
99 			gcmdp->cmd_dma_handle = NULL;
100 			return (FALSE);
101 		}
102 		break;
103 
104 	case DDI_DMA_NORESOURCES:
105 		bp->b_error = 0;
106 		ddi_dma_free_handle(&gcmdp->cmd_dma_handle);
107 		gcmdp->cmd_dma_handle = NULL;
108 		return (FALSE);
109 
110 	case DDI_DMA_TOOBIG:
111 		bioerror(bp, EINVAL);
112 		ddi_dma_free_handle(&gcmdp->cmd_dma_handle);
113 		gcmdp->cmd_dma_handle = NULL;
114 		return (FALSE);
115 
116 	case DDI_DMA_NOMAPPING:
117 	case DDI_DMA_INUSE:
118 	default:
119 		bioerror(bp, EFAULT);
120 		ddi_dma_free_handle(&gcmdp->cmd_dma_handle);
121 		gcmdp->cmd_dma_handle = NULL;
122 		return (FALSE);
123 	}
124 
125 	/* initialize the loop controls for ghd_dmaget_next_attr() */
126 	gcmdp->cmd_windex = 0;
127 	gcmdp->cmd_cindex = 0;
128 	gcmdp->cmd_totxfer = 0;
129 	gcmdp->cmd_dma_flags = dma_flags;
130 	gcmdp->use_first = 1;
131 	return (TRUE);
132 }
133 
134 
135 uint_t
136 ghd_dmaget_next_attr(ccc_t *cccp, gcmd_t *gcmdp, long max_transfer_cnt,
137     int sg_size, ddi_dma_cookie_t cookie)
138 {
139 	ulong_t	toxfer = 0;
140 	int	num_segs = 0;
141 	int	single_seg;
142 
143 	GDBG_DMA(("ghd_dma_attr_get: start: gcmdp 0x%p h 0x%p c 0x%x\n",
144 			gcmdp, gcmdp->cmd_dma_handle, gcmdp->cmd_ccount));
145 
146 	/*
147 	 * Disable single-segment Scatter/Gather option
148 	 * if can't do this transfer in a single segment,
149 	 */
150 	if (gcmdp->cmd_cindex + 1 < gcmdp->cmd_ccount) {
151 		single_seg = FALSE;
152 	} else {
153 		single_seg = TRUE;
154 	}
155 
156 
157 	for (;;) {
158 		/*
159 		 * call the controller specific S/G function
160 		 */
161 		(*cccp->ccc_sg_func)(gcmdp, &cookie, single_seg, num_segs);
162 
163 		/* take care of the loop-bookkeeping */
164 		toxfer += cookie.dmac_size;
165 		num_segs++;
166 		gcmdp->cmd_cindex++;
167 
168 		/*
169 		 * if this was the last cookie in the current window
170 		 * set the loop controls start the next window and
171 		 * exit so the HBA can do this partial transfer
172 		 */
173 		if (gcmdp->cmd_cindex >= gcmdp->cmd_ccount) {
174 			gcmdp->cmd_windex++;
175 			gcmdp->cmd_cindex = 0;
176 			break;
177 		}
178 		ASSERT(single_seg == FALSE);
179 
180 		if (toxfer >= max_transfer_cnt)
181 			break;
182 
183 		if (num_segs >= sg_size)
184 			break;
185 
186 		ddi_dma_nextcookie(gcmdp->cmd_dma_handle, &cookie);
187 	}
188 
189 	gcmdp->cmd_totxfer += toxfer;
190 
191 	return (toxfer);
192 }
193 
194 
195 
196 int
197 ghd_dmaget_attr(ccc_t		*cccp,
198 		gcmd_t		*gcmdp,
199 		long		count,
200 		int		sg_size,
201 		uint_t		*xfer)
202 {
203 	int	status;
204 	ddi_dma_cookie_t cookie;
205 
206 	*xfer = 0;
207 
208 
209 	if (gcmdp->use_first == 1) {
210 		cookie = gcmdp->cmd_first_cookie;
211 		gcmdp->use_first = 0;
212 	} else if (gcmdp->cmd_windex >= gcmdp->cmd_wcount) {
213 		/*
214 		 * reached the end of buffer. This should not happen.
215 		 */
216 		ASSERT(gcmdp->cmd_windex < gcmdp->cmd_wcount);
217 		return (FALSE);
218 
219 	} else if (gcmdp->cmd_cindex == 0) {
220 		off_t	offset;
221 		size_t	length;
222 
223 		/*
224 		 * start the next window, and get its first cookie
225 		 */
226 		status = ddi_dma_getwin(gcmdp->cmd_dma_handle,
227 				gcmdp->cmd_windex, &offset, &length,
228 				&cookie, &gcmdp->cmd_ccount);
229 		if (status != DDI_SUCCESS)
230 			return (FALSE);
231 
232 	} else {
233 		/*
234 		 * get the next cookie in the current window
235 		 */
236 		ddi_dma_nextcookie(gcmdp->cmd_dma_handle, &cookie);
237 	}
238 
239 	/*
240 	 * start the Scatter/Gather loop passing in the first
241 	 * cookie obtained above
242 	 */
243 	*xfer = ghd_dmaget_next_attr(cccp, gcmdp, count, sg_size, cookie);
244 	return (TRUE);
245 }
246