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
30void
31ghd_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
50int
51ghd_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
134uint_t
135ghd_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
195int
196ghd_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