Home | History | Annotate | Download | only in ioat
      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