diff options
Diffstat (limited to 'usr/src/uts/i86pc/io/ioat/ioat_ioctl.c')
| -rw-r--r-- | usr/src/uts/i86pc/io/ioat/ioat_ioctl.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/usr/src/uts/i86pc/io/ioat/ioat_ioctl.c b/usr/src/uts/i86pc/io/ioat/ioat_ioctl.c new file mode 100644 index 0000000000..70640dac4f --- /dev/null +++ b/usr/src/uts/i86pc/io/ioat/ioat_ioctl.c @@ -0,0 +1,343 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/conf.h> +#include <sys/kmem.h> +#include <sys/ddi.h> +#include <sys/stat.h> +#include <sys/sunddi.h> +#include <sys/file.h> +#include <sys/open.h> +#include <sys/modctl.h> +#include <sys/ddi_impldefs.h> +#include <sys/sysmacros.h> + +#include <vm/hat.h> +#include <vm/as.h> + +#include <sys/ioat.h> + + +extern void *ioat_statep; +#define ptob64(x) (((uint64_t)(x)) << PAGESHIFT) + +static int ioat_ioctl_rdreg(ioat_state_t *state, void *arg, int mode); +#ifdef DEBUG +static int ioat_ioctl_wrreg(ioat_state_t *state, void *arg, int mode); +static int ioat_ioctl_test(ioat_state_t *state, void *arg, int mode); +#endif + +/* + * ioat_ioctl() + */ +/*ARGSUSED*/ +int +ioat_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rval) +{ + ioat_state_t *state; + int instance; + int e; + + + e = drv_priv(cred); + if (e != 0) { + return (EPERM); + } + instance = getminor(dev); + if (instance == -1) { + return (EBADF); + } + state = ddi_get_soft_state(ioat_statep, instance); + if (state == NULL) { + return (EBADF); + } + + switch (cmd) { + case IOAT_IOCTL_READ_REG: + e = ioat_ioctl_rdreg(state, (void *)arg, mode); + break; +#ifdef DEBUG + case IOAT_IOCTL_WRITE_REG: + e = ioat_ioctl_wrreg(state, (void *)arg, mode); + break; + case IOAT_IOCTL_TEST: + e = ioat_ioctl_test(state, (void *)arg, mode); + break; +#endif + + default: + e = ENXIO; + } + + return (e); +} + + +/* + * ioat_ioctl_rdreg() + */ +static int +ioat_ioctl_rdreg(ioat_state_t *state, void *arg, int mode) +{ + ioat_ioctl_rdreg_t rdreg; + int e; + + + e = ddi_copyin(arg, &rdreg, sizeof (ioat_ioctl_rdreg_t), mode); + if (e != 0) { + return (EFAULT); + } + + /* + * read a device register, where size is read size in bits, addr is + * the offset into MMIO registers. + */ + switch (rdreg.size) { + case 8: + rdreg.data = (uint64_t)ddi_get8(state->is_reg_handle, + (uint8_t *)&state->is_genregs[rdreg.addr]); + break; + case 16: + rdreg.data = (uint64_t)ddi_get16(state->is_reg_handle, + (uint16_t *)&state->is_genregs[rdreg.addr]); + break; + case 32: + rdreg.data = (uint64_t)ddi_get32(state->is_reg_handle, + (uint32_t *)&state->is_genregs[rdreg.addr]); + break; + case 64: + rdreg.data = (uint64_t)ddi_get64(state->is_reg_handle, + (uint64_t *)&state->is_genregs[rdreg.addr]); + break; + default: + return (EFAULT); + } + + e = ddi_copyout(&rdreg, arg, sizeof (ioat_ioctl_rdreg_t), mode); + if (e != 0) { + return (EFAULT); + } + + return (0); +} + + +#ifdef DEBUG +/* + * ioat_ioctl_wrreg() + */ +static int +ioat_ioctl_wrreg(ioat_state_t *state, void *arg, int mode) +{ + ioat_ioctl_wrreg_t wrreg; + int e; + + + e = ddi_copyin(arg, &wrreg, sizeof (ioat_ioctl_wrreg_t), mode); + if (e != 0) { + return (EFAULT); + } + + /* + * write a device register, where size is write size in bits, addr is + * the offset into MMIO registers. + */ + switch (wrreg.size) { + case 8: + ddi_put8(state->is_reg_handle, + (uint8_t *)&state->is_genregs[wrreg.addr], + (uint8_t)wrreg.data); + break; + case 16: + ddi_put16(state->is_reg_handle, + (uint16_t *)&state->is_genregs[wrreg.addr], + (uint16_t)wrreg.data); + break; + case 32: + ddi_put32(state->is_reg_handle, + (uint32_t *)&state->is_genregs[wrreg.addr], + (uint32_t)wrreg.data); + break; + case 64: + ddi_put64(state->is_reg_handle, + (uint64_t *)&state->is_genregs[wrreg.addr], + (uint64_t)wrreg.data); + break; + default: + return (EFAULT); + } + + return (0); +} + + +/* + * ioat_ioctl_test() + */ +/*ARGSUSED*/ +static int +ioat_ioctl_test(ioat_state_t *state, void *arg, int mode) +{ + dcopy_handle_t channel; + dcopy_cmd_t cmd; + uint8_t *source; + uint_t buf_size; + uint_t poll_cnt; + uint8_t *dest; + uint8_t *buf; + int flags; + int i; + int e; + + + /* allocate 2 paged aligned 4k pages */ + buf_size = 0x1000; + buf = kmem_zalloc((buf_size * 2) + 0x1000, KM_SLEEP); + source = (uint8_t *)(((uintptr_t)buf + PAGEOFFSET) & PAGEMASK); + dest = source + buf_size; + + /* Init source buffer */ + for (i = 0; i < buf_size; i++) { + source[i] = (uint8_t)(i & 0xFF); + } + + /* allocate a DMA channel */ + e = dcopy_alloc(DCOPY_SLEEP, &channel); + if (e != DCOPY_SUCCESS) { + cmn_err(CE_CONT, "dcopy_alloc() failed\n"); + goto testfail_alloc; + } + + /* + * post 32 DMA copy's from dest to dest. These will complete in order + * so they won't stomp on each other. We don't care about the data + * right now which is why we go dest to dest. + */ + flags = DCOPY_SLEEP; + for (i = 0; i < 32; i++) { + /* + * if this is the second command, link the commands from here + * on out. We only want to keep track of the last command. We + * will poll on the last command completing (which infers that + * the other commands completed). If any of the previous + * commands fail, so will the last one. Linking the commands + * also allows us to only call free for the last command. free + * will free up the entire chain of commands. + */ + if (i == 1) { + flags |= DCOPY_ALLOC_LINK; + } + e = dcopy_cmd_alloc(channel, flags, &cmd); + if (e != DCOPY_SUCCESS) { + cmn_err(CE_CONT, "dcopy_cmd_alloc() failed\n"); + goto testfail_alloc; + } + + ASSERT(cmd->dp_version == DCOPY_CMD_V0); + cmd->dp_cmd = DCOPY_CMD_COPY; + cmd->dp_flags = DCOPY_CMD_NOFLAGS; + + /* do a bunch of dest to dest DMA's */ + cmd->dp.copy.cc_source = ptob64(hat_getpfnum(kas.a_hat, + (caddr_t)source)) + ((uintptr_t)dest & PAGEOFFSET); + cmd->dp.copy.cc_dest = ptob64(hat_getpfnum(kas.a_hat, + (caddr_t)dest)) + ((uintptr_t)dest & PAGEOFFSET); + cmd->dp.copy.cc_size = PAGESIZE; + + e = dcopy_cmd_post(cmd); + if (e != DCOPY_SUCCESS) { + cmn_err(CE_CONT, "dcopy_post() failed\n"); + goto testfail_post; + } + } + + e = dcopy_cmd_alloc(channel, flags, &cmd); + if (e != DCOPY_SUCCESS) { + cmn_err(CE_CONT, "dcopy_cmd_alloc() failed\n"); + goto testfail_alloc; + } + + /* now queue up the DMA we are going to check status and data for */ + cmd->dp_cmd = DCOPY_CMD_COPY; + cmd->dp_flags = DCOPY_CMD_INTR; + cmd->dp.copy.cc_source = ptob64(hat_getpfnum(kas.a_hat, + (caddr_t)source)) + ((uintptr_t)source & PAGEOFFSET); + cmd->dp.copy.cc_dest = ptob64(hat_getpfnum(kas.a_hat, + (caddr_t)dest)) + ((uintptr_t)dest & PAGEOFFSET); + cmd->dp.copy.cc_size = PAGESIZE; + e = dcopy_cmd_post(cmd); + if (e != DCOPY_SUCCESS) { + cmn_err(CE_CONT, "dcopy_post() failed\n"); + goto testfail_post; + } + + /* check the status of the last command */ + poll_cnt = 0; + flags = DCOPY_POLL_NOFLAGS; + while ((e = dcopy_cmd_poll(cmd, flags)) == DCOPY_PENDING) { + poll_cnt++; + if (poll_cnt >= 16) { + flags |= DCOPY_POLL_BLOCK; + } + } + if (e != DCOPY_COMPLETED) { + cmn_err(CE_CONT, "dcopy_poll() failed\n"); + goto testfail_poll; + } + + /* since the cmd's are linked we only need to pass in the last cmd */ + dcopy_cmd_free(&cmd); + dcopy_free(&channel); + + /* verify the data */ + for (i = 0; i < PAGESIZE; i++) { + if (dest[i] != (uint8_t)(i & 0xFF)) { + cmn_err(CE_CONT, + "dcopy_data_compare() failed, %p[%d]: %x, %x\n", + (void *)dest, i, dest[i], i & 0xFF); + return (-1); + } + } + + kmem_free(buf, (buf_size * 2) + 0x1000); + + return (0); + +testfail_data_compare: +testfail_poll: +testfail_post: + dcopy_cmd_free(&cmd); + dcopy_free(&channel); +testfail_alloc: + kmem_free(buf, (buf_size * 2) + 0x1000); + + return (-1); +} +#endif |
