summaryrefslogtreecommitdiff
path: root/usr/src/uts/i86pc/io/ioat/ioat_ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/i86pc/io/ioat/ioat_ioctl.c')
-rw-r--r--usr/src/uts/i86pc/io/ioat/ioat_ioctl.c343
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