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 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 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 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 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