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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/errno.h> 30 #include <sys/types.h> 31 #include <sys/conf.h> 32 #include <sys/kmem.h> 33 #include <sys/ddi.h> 34 #include <sys/stat.h> 35 #include <sys/sunddi.h> 36 #include <sys/file.h> 37 #include <sys/open.h> 38 #include <sys/modctl.h> 39 #include <sys/ddi_impldefs.h> 40 #include <sys/sysmacros.h> 41 42 #include <vm/hat.h> 43 #include <vm/as.h> 44 45 #include <sys/ioat.h> 46 47 48 extern void *ioat_statep; 49 #define ptob64(x) (((uint64_t)(x)) << PAGESHIFT) 50 51 static int ioat_ioctl_rdreg(ioat_state_t *state, void *arg, int mode); 52 #ifdef DEBUG 53 static int ioat_ioctl_wrreg(ioat_state_t *state, void *arg, int mode); 54 static int ioat_ioctl_test(ioat_state_t *state, void *arg, int mode); 55 #endif 56 57 /* 58 * ioat_ioctl() 59 */ 60 /*ARGSUSED*/ 61 int 62 ioat_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rval) 63 { 64 ioat_state_t *state; 65 int instance; 66 int e; 67 68 69 e = drv_priv(cred); 70 if (e != 0) { 71 return (EPERM); 72 } 73 instance = getminor(dev); 74 if (instance == -1) { 75 return (EBADF); 76 } 77 state = ddi_get_soft_state(ioat_statep, instance); 78 if (state == NULL) { 79 return (EBADF); 80 } 81 82 switch (cmd) { 83 case IOAT_IOCTL_READ_REG: 84 e = ioat_ioctl_rdreg(state, (void *)arg, mode); 85 break; 86 #ifdef DEBUG 87 case IOAT_IOCTL_WRITE_REG: 88 e = ioat_ioctl_wrreg(state, (void *)arg, mode); 89 break; 90 case IOAT_IOCTL_TEST: 91 e = ioat_ioctl_test(state, (void *)arg, mode); 92 break; 93 #endif 94 95 default: 96 e = ENXIO; 97 } 98 99 return (e); 100 } 101 102 103 /* 104 * ioat_ioctl_rdreg() 105 */ 106 static int 107 ioat_ioctl_rdreg(ioat_state_t *state, void *arg, int mode) 108 { 109 ioat_ioctl_rdreg_t rdreg; 110 int e; 111 112 113 e = ddi_copyin(arg, &rdreg, sizeof (ioat_ioctl_rdreg_t), mode); 114 if (e != 0) { 115 return (EFAULT); 116 } 117 118 /* 119 * read a device register, where size is read size in bits, addr is 120 * the offset into MMIO registers. 121 */ 122 switch (rdreg.size) { 123 case 8: 124 rdreg.data = (uint64_t)ddi_get8(state->is_reg_handle, 125 (uint8_t *)&state->is_genregs[rdreg.addr]); 126 break; 127 case 16: 128 rdreg.data = (uint64_t)ddi_get16(state->is_reg_handle, 129 (uint16_t *)&state->is_genregs[rdreg.addr]); 130 break; 131 case 32: 132 rdreg.data = (uint64_t)ddi_get32(state->is_reg_handle, 133 (uint32_t *)&state->is_genregs[rdreg.addr]); 134 break; 135 case 64: 136 rdreg.data = (uint64_t)ddi_get64(state->is_reg_handle, 137 (uint64_t *)&state->is_genregs[rdreg.addr]); 138 break; 139 default: 140 return (EFAULT); 141 } 142 143 e = ddi_copyout(&rdreg, arg, sizeof (ioat_ioctl_rdreg_t), mode); 144 if (e != 0) { 145 return (EFAULT); 146 } 147 148 return (0); 149 } 150 151 152 #ifdef DEBUG 153 /* 154 * ioat_ioctl_wrreg() 155 */ 156 static int 157 ioat_ioctl_wrreg(ioat_state_t *state, void *arg, int mode) 158 { 159 ioat_ioctl_wrreg_t wrreg; 160 int e; 161 162 163 e = ddi_copyin(arg, &wrreg, sizeof (ioat_ioctl_wrreg_t), mode); 164 if (e != 0) { 165 return (EFAULT); 166 } 167 168 /* 169 * write a device register, where size is write size in bits, addr is 170 * the offset into MMIO registers. 171 */ 172 switch (wrreg.size) { 173 case 8: 174 ddi_put8(state->is_reg_handle, 175 (uint8_t *)&state->is_genregs[wrreg.addr], 176 (uint8_t)wrreg.data); 177 break; 178 case 16: 179 ddi_put16(state->is_reg_handle, 180 (uint16_t *)&state->is_genregs[wrreg.addr], 181 (uint16_t)wrreg.data); 182 break; 183 case 32: 184 ddi_put32(state->is_reg_handle, 185 (uint32_t *)&state->is_genregs[wrreg.addr], 186 (uint32_t)wrreg.data); 187 break; 188 case 64: 189 ddi_put64(state->is_reg_handle, 190 (uint64_t *)&state->is_genregs[wrreg.addr], 191 (uint64_t)wrreg.data); 192 break; 193 default: 194 return (EFAULT); 195 } 196 197 return (0); 198 } 199 200 201 /* 202 * ioat_ioctl_test() 203 */ 204 /*ARGSUSED*/ 205 static int 206 ioat_ioctl_test(ioat_state_t *state, void *arg, int mode) 207 { 208 dcopy_handle_t channel; 209 dcopy_cmd_t cmd; 210 uint8_t *source; 211 uint_t buf_size; 212 uint_t poll_cnt; 213 uint8_t *dest; 214 uint8_t *buf; 215 int flags; 216 int i; 217 int e; 218 219 220 /* allocate 2 paged aligned 4k pages */ 221 buf_size = 0x1000; 222 buf = kmem_zalloc((buf_size * 2) + 0x1000, KM_SLEEP); 223 source = (uint8_t *)(((uintptr_t)buf + PAGEOFFSET) & PAGEMASK); 224 dest = source + buf_size; 225 226 /* Init source buffer */ 227 for (i = 0; i < buf_size; i++) { 228 source[i] = (uint8_t)(i & 0xFF); 229 } 230 231 /* allocate a DMA channel */ 232 e = dcopy_alloc(DCOPY_SLEEP, &channel); 233 if (e != DCOPY_SUCCESS) { 234 cmn_err(CE_CONT, "dcopy_alloc() failed\n"); 235 goto testfail_alloc; 236 } 237 238 /* 239 * post 32 DMA copy's from dest to dest. These will complete in order 240 * so they won't stomp on each other. We don't care about the data 241 * right now which is why we go dest to dest. 242 */ 243 flags = DCOPY_SLEEP; 244 for (i = 0; i < 32; i++) { 245 /* 246 * if this is the second command, link the commands from here 247 * on out. We only want to keep track of the last command. We 248 * will poll on the last command completing (which infers that 249 * the other commands completed). If any of the previous 250 * commands fail, so will the last one. Linking the commands 251 * also allows us to only call free for the last command. free 252 * will free up the entire chain of commands. 253 */ 254 if (i == 1) { 255 flags |= DCOPY_ALLOC_LINK; 256 } 257 e = dcopy_cmd_alloc(channel, flags, &cmd); 258 if (e != DCOPY_SUCCESS) { 259 cmn_err(CE_CONT, "dcopy_cmd_alloc() failed\n"); 260 goto testfail_alloc; 261 } 262 263 ASSERT(cmd->dp_version == DCOPY_CMD_V0); 264 cmd->dp_cmd = DCOPY_CMD_COPY; 265 cmd->dp_flags = DCOPY_CMD_NOFLAGS; 266 267 /* do a bunch of dest to dest DMA's */ 268 cmd->dp.copy.cc_source = ptob64(hat_getpfnum(kas.a_hat, 269 (caddr_t)source)) + ((uintptr_t)dest & PAGEOFFSET); 270 cmd->dp.copy.cc_dest = ptob64(hat_getpfnum(kas.a_hat, 271 (caddr_t)dest)) + ((uintptr_t)dest & PAGEOFFSET); 272 cmd->dp.copy.cc_size = PAGESIZE; 273 274 e = dcopy_cmd_post(cmd); 275 if (e != DCOPY_SUCCESS) { 276 cmn_err(CE_CONT, "dcopy_post() failed\n"); 277 goto testfail_post; 278 } 279 } 280 281 e = dcopy_cmd_alloc(channel, flags, &cmd); 282 if (e != DCOPY_SUCCESS) { 283 cmn_err(CE_CONT, "dcopy_cmd_alloc() failed\n"); 284 goto testfail_alloc; 285 } 286 287 /* now queue up the DMA we are going to check status and data for */ 288 cmd->dp_cmd = DCOPY_CMD_COPY; 289 cmd->dp_flags = DCOPY_CMD_INTR; 290 cmd->dp.copy.cc_source = ptob64(hat_getpfnum(kas.a_hat, 291 (caddr_t)source)) + ((uintptr_t)source & PAGEOFFSET); 292 cmd->dp.copy.cc_dest = ptob64(hat_getpfnum(kas.a_hat, 293 (caddr_t)dest)) + ((uintptr_t)dest & PAGEOFFSET); 294 cmd->dp.copy.cc_size = PAGESIZE; 295 e = dcopy_cmd_post(cmd); 296 if (e != DCOPY_SUCCESS) { 297 cmn_err(CE_CONT, "dcopy_post() failed\n"); 298 goto testfail_post; 299 } 300 301 /* check the status of the last command */ 302 poll_cnt = 0; 303 flags = DCOPY_POLL_NOFLAGS; 304 while ((e = dcopy_cmd_poll(cmd, flags)) == DCOPY_PENDING) { 305 poll_cnt++; 306 if (poll_cnt >= 16) { 307 flags |= DCOPY_POLL_BLOCK; 308 } 309 } 310 if (e != DCOPY_COMPLETED) { 311 cmn_err(CE_CONT, "dcopy_poll() failed\n"); 312 goto testfail_poll; 313 } 314 315 /* since the cmd's are linked we only need to pass in the last cmd */ 316 dcopy_cmd_free(&cmd); 317 dcopy_free(&channel); 318 319 /* verify the data */ 320 for (i = 0; i < PAGESIZE; i++) { 321 if (dest[i] != (uint8_t)(i & 0xFF)) { 322 cmn_err(CE_CONT, 323 "dcopy_data_compare() failed, %p[%d]: %x, %x\n", 324 (void *)dest, i, dest[i], i & 0xFF); 325 return (-1); 326 } 327 } 328 329 kmem_free(buf, (buf_size * 2) + 0x1000); 330 331 return (0); 332 333 testfail_data_compare: 334 testfail_poll: 335 testfail_post: 336 dcopy_cmd_free(&cmd); 337 dcopy_free(&channel); 338 testfail_alloc: 339 kmem_free(buf, (buf_size * 2) + 0x1000); 340 341 return (-1); 342 } 343 #endif 344