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