xref: /titanic_50/usr/src/uts/i86pc/io/ioat/ioat_chan.c (revision eca2601cae391051acb146d28fba04237fe1eb85)
117169044Sbrutus /*
217169044Sbrutus  * CDDL HEADER START
317169044Sbrutus  *
417169044Sbrutus  * The contents of this file are subject to the terms of the
517169044Sbrutus  * Common Development and Distribution License (the "License").
617169044Sbrutus  * You may not use this file except in compliance with the License.
717169044Sbrutus  *
817169044Sbrutus  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
917169044Sbrutus  * or http://www.opensolaris.org/os/licensing.
1017169044Sbrutus  * See the License for the specific language governing permissions
1117169044Sbrutus  * and limitations under the License.
1217169044Sbrutus  *
1317169044Sbrutus  * When distributing Covered Code, include this CDDL HEADER in each
1417169044Sbrutus  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1517169044Sbrutus  * If applicable, add the following below this CDDL HEADER, with the
1617169044Sbrutus  * fields enclosed by brackets "[]" replaced with your own identifying
1717169044Sbrutus  * information: Portions Copyright [yyyy] [name of copyright owner]
1817169044Sbrutus  *
1917169044Sbrutus  * CDDL HEADER END
2017169044Sbrutus  */
2117169044Sbrutus 
2217169044Sbrutus /*
234f0f65c2SMark Johnson  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2417169044Sbrutus  * Use is subject to license terms.
2517169044Sbrutus  */
2617169044Sbrutus 
27*eca2601cSRandy Fishel /*
28*eca2601cSRandy Fishel  * Copyright (c) 2009, Intel Corporation.
29*eca2601cSRandy Fishel  * All rights reserved.
30*eca2601cSRandy Fishel  */
31*eca2601cSRandy Fishel 
3217169044Sbrutus #include <sys/errno.h>
3317169044Sbrutus #include <sys/types.h>
3417169044Sbrutus #include <sys/conf.h>
3517169044Sbrutus #include <sys/kmem.h>
3617169044Sbrutus #include <sys/ddi.h>
3717169044Sbrutus #include <sys/stat.h>
3817169044Sbrutus #include <sys/sunddi.h>
3917169044Sbrutus #include <sys/file.h>
4017169044Sbrutus #include <sys/open.h>
4117169044Sbrutus #include <sys/modctl.h>
4217169044Sbrutus #include <sys/ddi_impldefs.h>
4317169044Sbrutus #include <sys/sysmacros.h>
4417169044Sbrutus #include <vm/hat.h>
4517169044Sbrutus #include <vm/as.h>
4617169044Sbrutus #include <sys/mach_mmu.h>
4717169044Sbrutus #ifdef __xpv
4817169044Sbrutus #include <sys/hypervisor.h>
4917169044Sbrutus #endif
5017169044Sbrutus 
5117169044Sbrutus #include <sys/ioat.h>
5217169044Sbrutus 
5317169044Sbrutus 
5417169044Sbrutus extern ddi_device_acc_attr_t ioat_acc_attr;
5517169044Sbrutus 
5617169044Sbrutus /* dma attr for the descriptor rings */
5717169044Sbrutus ddi_dma_attr_t ioat_desc_dma_attr = {
5817169044Sbrutus 	DMA_ATTR_V0,		/* dma_attr_version */
5917169044Sbrutus 	0x0,			/* dma_attr_addr_lo */
6017169044Sbrutus 	0xffffffffffffffff,	/* dma_attr_addr_hi */
6117169044Sbrutus 	0xffffffff,		/* dma_attr_count_max */
6217169044Sbrutus 	0x1000,			/* dma_attr_align */
6317169044Sbrutus 	0x1,			/* dma_attr_burstsizes */
6417169044Sbrutus 	0x1,			/* dma_attr_minxfer */
6517169044Sbrutus 	0xffffffff,		/* dma_attr_maxxfer */
6617169044Sbrutus 	0xffffffff,		/* dma_attr_seg */
6717169044Sbrutus 	0x1,			/* dma_attr_sgllen */
6817169044Sbrutus 	0x1,			/* dma_attr_granular */
6917169044Sbrutus 	0x0,			/* dma_attr_flags */
7017169044Sbrutus };
7117169044Sbrutus 
7217169044Sbrutus /* dma attr for the completion buffers */
7317169044Sbrutus ddi_dma_attr_t ioat_cmpl_dma_attr = {
7417169044Sbrutus 	DMA_ATTR_V0,		/* dma_attr_version */
7517169044Sbrutus 	0x0,			/* dma_attr_addr_lo */
7617169044Sbrutus 	0xffffffffffffffff,	/* dma_attr_addr_hi */
7717169044Sbrutus 	0xffffffff,		/* dma_attr_count_max */
7817169044Sbrutus 	0x40,			/* dma_attr_align */
7917169044Sbrutus 	0x1,			/* dma_attr_burstsizes */
8017169044Sbrutus 	0x1,			/* dma_attr_minxfer */
8117169044Sbrutus 	0xffffffff,		/* dma_attr_maxxfer */
8217169044Sbrutus 	0xffffffff,		/* dma_attr_seg */
8317169044Sbrutus 	0x1,			/* dma_attr_sgllen */
8417169044Sbrutus 	0x1,			/* dma_attr_granular */
8517169044Sbrutus 	0x0,			/* dma_attr_flags */
8617169044Sbrutus };
8717169044Sbrutus 
8817169044Sbrutus static int ioat_completion_alloc(ioat_channel_t channel);
8917169044Sbrutus static void ioat_completion_free(ioat_channel_t channel);
9017169044Sbrutus static void ioat_channel_start(ioat_channel_t channel);
9117169044Sbrutus static void ioat_channel_reset(ioat_channel_t channel);
9217169044Sbrutus 
9317169044Sbrutus int ioat_ring_alloc(ioat_channel_t channel, uint_t desc_cnt);
9417169044Sbrutus void ioat_ring_free(ioat_channel_t channel);
9517169044Sbrutus void ioat_ring_seed(ioat_channel_t channel, ioat_chan_dma_desc_t *desc);
9617169044Sbrutus int ioat_ring_reserve(ioat_channel_t channel, ioat_channel_ring_t *ring,
9717169044Sbrutus     dcopy_cmd_t cmd);
9817169044Sbrutus 
9917169044Sbrutus static void ioat_cmd_post_copy(ioat_channel_ring_t *ring, uint64_t src_addr,
10017169044Sbrutus     uint64_t dest_addr, uint32_t size, uint32_t ctrl);
10117169044Sbrutus static void ioat_cmd_post_dca(ioat_channel_ring_t *ring, uint32_t dca_id);
10217169044Sbrutus 
10317169044Sbrutus 
10417169044Sbrutus /*
10517169044Sbrutus  * ioat_channel_init()
10617169044Sbrutus  */
10717169044Sbrutus int
ioat_channel_init(ioat_state_t * state)10817169044Sbrutus ioat_channel_init(ioat_state_t *state)
10917169044Sbrutus {
11017169044Sbrutus 	int i;
11117169044Sbrutus 
11217169044Sbrutus 	/*
11317169044Sbrutus 	 * initialize each dma channel's state which doesn't change across
11417169044Sbrutus 	 * channel alloc/free.
11517169044Sbrutus 	 */
11617169044Sbrutus 	state->is_chansize = sizeof (struct ioat_channel_s) *
11717169044Sbrutus 	    state->is_num_channels;
11817169044Sbrutus 	state->is_channel = kmem_zalloc(state->is_chansize, KM_SLEEP);
11917169044Sbrutus 	for (i = 0; i < state->is_num_channels; i++) {
12017169044Sbrutus 		state->is_channel[i].ic_state = state;
12117169044Sbrutus 		state->is_channel[i].ic_regs = (uint8_t *)
12217169044Sbrutus 		    ((uintptr_t)state->is_genregs +
12317169044Sbrutus 		    (uintptr_t)(IOAT_CHANNELREG_OFFSET * (i + 1)));
12417169044Sbrutus 	}
12517169044Sbrutus 
12617169044Sbrutus 	/* initial the allocator (from 0 to state->is_num_channels) */
12717169044Sbrutus 	ioat_rs_init(state, 0, state->is_num_channels, &state->is_channel_rs);
12817169044Sbrutus 
12917169044Sbrutus 	return (DDI_SUCCESS);
13017169044Sbrutus }
13117169044Sbrutus 
13217169044Sbrutus 
13317169044Sbrutus /*
13417169044Sbrutus  * ioat_channel_fini()
13517169044Sbrutus  */
13617169044Sbrutus void
ioat_channel_fini(ioat_state_t * state)13717169044Sbrutus ioat_channel_fini(ioat_state_t *state)
13817169044Sbrutus {
13917169044Sbrutus 	ioat_rs_fini(&state->is_channel_rs);
14017169044Sbrutus 	kmem_free(state->is_channel, state->is_chansize);
14117169044Sbrutus }
14217169044Sbrutus 
14317169044Sbrutus 
14417169044Sbrutus /*
14517169044Sbrutus  * ioat_channel_alloc()
14617169044Sbrutus  *   NOTE: We intentionaly don't handle DCOPY_SLEEP (if no channels are
14717169044Sbrutus  *	available)
14817169044Sbrutus  */
14917169044Sbrutus /*ARGSUSED*/
15017169044Sbrutus int
ioat_channel_alloc(void * device_private,dcopy_handle_t handle,int flags,uint_t size,dcopy_query_channel_t * info,void * channel_private)15117169044Sbrutus ioat_channel_alloc(void *device_private, dcopy_handle_t handle, int flags,
15217169044Sbrutus     uint_t size, dcopy_query_channel_t *info, void *channel_private)
15317169044Sbrutus {
15417169044Sbrutus #define	CHANSTRSIZE	20
15517169044Sbrutus 	struct ioat_channel_s *channel;
15617169044Sbrutus 	char chanstr[CHANSTRSIZE];
15717169044Sbrutus 	ioat_channel_t *chan;
15817169044Sbrutus 	ioat_state_t *state;
15917169044Sbrutus 	size_t cmd_size;
16017169044Sbrutus 	uint_t chan_num;
16117169044Sbrutus 	uint32_t estat;
16217169044Sbrutus 	int e;
16317169044Sbrutus 
16417169044Sbrutus 
16517169044Sbrutus 	state = (ioat_state_t *)device_private;
16617169044Sbrutus 	chan = (ioat_channel_t *)channel_private;
16717169044Sbrutus 
16817169044Sbrutus 	/* allocate a H/W channel */
16917169044Sbrutus 	e = ioat_rs_alloc(state->is_channel_rs, &chan_num);
17017169044Sbrutus 	if (e != DDI_SUCCESS) {
17117169044Sbrutus 		return (DCOPY_NORESOURCES);
17217169044Sbrutus 	}
17317169044Sbrutus 
17417169044Sbrutus 	channel = &state->is_channel[chan_num];
17517169044Sbrutus 	channel->ic_inuse = B_TRUE;
17617169044Sbrutus 	channel->ic_chan_num = chan_num;
17717169044Sbrutus 	channel->ic_ver = state->is_ver;
17817169044Sbrutus 	channel->ic_dca_active = B_FALSE;
17917169044Sbrutus 	channel->ic_channel_state = IOAT_CHANNEL_OK;
18017169044Sbrutus 	channel->ic_dcopy_handle = handle;
18117169044Sbrutus 
18217169044Sbrutus #ifdef	DEBUG
18317169044Sbrutus 	{
18417169044Sbrutus 		/* if we're cbv2, verify that the V2 compatibility bit is set */
18517169044Sbrutus 		uint16_t reg;
18617169044Sbrutus 		if (channel->ic_ver == IOAT_CBv2) {
18717169044Sbrutus 			reg = ddi_get16(state->is_reg_handle,
18817169044Sbrutus 			    (uint16_t *)&channel->ic_regs[IOAT_CHAN_COMP]);
18917169044Sbrutus 			ASSERT(reg & 0x2);
19017169044Sbrutus 		}
19117169044Sbrutus 	}
19217169044Sbrutus #endif
19317169044Sbrutus 
19417169044Sbrutus 	/*
19517169044Sbrutus 	 * Configure DMA channel
19617169044Sbrutus 	 *   Channel In Use
19717169044Sbrutus 	 *   Error Interrupt Enable
19817169044Sbrutus 	 *   Any Error Abort Enable
19917169044Sbrutus 	 *   Error Completion Enable
20017169044Sbrutus 	 */
20117169044Sbrutus 	ddi_put16(state->is_reg_handle,
20217169044Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x011C);
20317169044Sbrutus 
20417169044Sbrutus 	/* check channel error register, clear any errors */
20517169044Sbrutus 	estat = ddi_get32(state->is_reg_handle,
20617169044Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
20717169044Sbrutus 	if (estat != 0) {
20817169044Sbrutus #ifdef	DEBUG
20917169044Sbrutus 		cmn_err(CE_CONT, "cleared errors (0x%x) before channel (%d) "
21017169044Sbrutus 		    "enable\n", estat, channel->ic_chan_num);
21117169044Sbrutus #endif
21217169044Sbrutus 		ddi_put32(state->is_reg_handle,
21317169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR], estat);
21417169044Sbrutus 	}
21517169044Sbrutus 
21617169044Sbrutus 	/* allocate and initialize the descriptor buf */
21717169044Sbrutus 	e = ioat_ring_alloc(channel, size);
21817169044Sbrutus 	if (e != DDI_SUCCESS) {
21917169044Sbrutus 		goto chinitfail_desc_alloc;
22017169044Sbrutus 	}
22117169044Sbrutus 
22217169044Sbrutus 	/* allocate and initialize the completion space */
22317169044Sbrutus 	e = ioat_completion_alloc(channel);
22417169044Sbrutus 	if (e != DDI_SUCCESS) {
22517169044Sbrutus 		goto chinitfail_completion_alloc;
22617169044Sbrutus 	}
22717169044Sbrutus 
22817169044Sbrutus 	/* setup kmem_cache for commands */
22917169044Sbrutus 	cmd_size = sizeof (struct dcopy_cmd_s) +
23017169044Sbrutus 	    sizeof (struct dcopy_cmd_priv_s) +
23117169044Sbrutus 	    sizeof (struct ioat_cmd_private_s);
23217169044Sbrutus 	(void) snprintf(chanstr, CHANSTRSIZE, "ioat%dchan%dcmd",
23317169044Sbrutus 	    state->is_instance, channel->ic_chan_num);
23417169044Sbrutus 	channel->ic_cmd_cache = kmem_cache_create(chanstr, cmd_size, 64,
23517169044Sbrutus 	    NULL, NULL, NULL, NULL, NULL, 0);
23617169044Sbrutus 	if (channel->ic_cmd_cache == NULL) {
23717169044Sbrutus 		goto chinitfail_kmem_cache;
23817169044Sbrutus 	}
23917169044Sbrutus 
24017169044Sbrutus 	/* start-up the channel */
24117169044Sbrutus 	ioat_channel_start(channel);
24217169044Sbrutus 
24317169044Sbrutus 	/* fill in the channel info returned to dcopy */
24417169044Sbrutus 	info->qc_version = DCOPY_QUERY_CHANNEL_V0;
24517169044Sbrutus 	info->qc_id = state->is_deviceinfo.di_id;
24617169044Sbrutus 	info->qc_capabilities = (uint64_t)state->is_capabilities;
24717169044Sbrutus 	info->qc_channel_size = (uint64_t)size;
24817169044Sbrutus 	info->qc_chan_num = (uint64_t)channel->ic_chan_num;
24917169044Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
25017169044Sbrutus 		info->qc_dca_supported = B_FALSE;
25117169044Sbrutus 	} else {
25217169044Sbrutus 		if (info->qc_capabilities & IOAT_DMACAP_DCA) {
25317169044Sbrutus 			info->qc_dca_supported = B_TRUE;
25417169044Sbrutus 		} else {
25517169044Sbrutus 			info->qc_dca_supported = B_FALSE;
25617169044Sbrutus 		}
25717169044Sbrutus 	}
25817169044Sbrutus 
25917169044Sbrutus 	*chan = channel;
26017169044Sbrutus 
26117169044Sbrutus 	return (DCOPY_SUCCESS);
26217169044Sbrutus 
26317169044Sbrutus chinitfail_kmem_cache:
26417169044Sbrutus 	ioat_completion_free(channel);
26517169044Sbrutus chinitfail_completion_alloc:
26617169044Sbrutus 	ioat_ring_free(channel);
26717169044Sbrutus chinitfail_desc_alloc:
26817169044Sbrutus 	return (DCOPY_FAILURE);
26917169044Sbrutus }
27017169044Sbrutus 
27117169044Sbrutus 
27217169044Sbrutus /*
27317169044Sbrutus  * ioat_channel_suspend()
27417169044Sbrutus  */
27517169044Sbrutus /*ARGSUSED*/
27617169044Sbrutus void
ioat_channel_suspend(ioat_state_t * state)27717169044Sbrutus ioat_channel_suspend(ioat_state_t *state)
27817169044Sbrutus {
27917169044Sbrutus 	/*
28017169044Sbrutus 	 * normally you would disable interrupts and reset the H/W here. But
28117169044Sbrutus 	 * since the suspend framework doesn't know who is using us, it may
28217169044Sbrutus 	 * not suspend their I/O before us.  Since we won't actively be doing
28317169044Sbrutus 	 * any DMA or interrupts unless someone asks us to, it's safe to not
28417169044Sbrutus 	 * do anything here.
28517169044Sbrutus 	 */
28617169044Sbrutus }
28717169044Sbrutus 
28817169044Sbrutus 
28917169044Sbrutus /*
29017169044Sbrutus  * ioat_channel_resume()
29117169044Sbrutus  */
29217169044Sbrutus int
ioat_channel_resume(ioat_state_t * state)29317169044Sbrutus ioat_channel_resume(ioat_state_t *state)
29417169044Sbrutus {
29517169044Sbrutus 	ioat_channel_ring_t *ring;
29617169044Sbrutus 	ioat_channel_t channel;
29717169044Sbrutus 	uint32_t estat;
29817169044Sbrutus 	int i;
29917169044Sbrutus 
30017169044Sbrutus 
30117169044Sbrutus 	for (i = 0; i < state->is_num_channels; i++) {
30217169044Sbrutus 		channel = &state->is_channel[i];
30317169044Sbrutus 		ring = channel->ic_ring;
30417169044Sbrutus 
30517169044Sbrutus 		if (!channel->ic_inuse) {
30617169044Sbrutus 			continue;
30717169044Sbrutus 		}
30817169044Sbrutus 
30917169044Sbrutus 		/*
31017169044Sbrutus 		 * Configure DMA channel
31117169044Sbrutus 		 *   Channel In Use
31217169044Sbrutus 		 *   Error Interrupt Enable
31317169044Sbrutus 		 *   Any Error Abort Enable
31417169044Sbrutus 		 *   Error Completion Enable
31517169044Sbrutus 		 */
31617169044Sbrutus 		ddi_put16(state->is_reg_handle,
31717169044Sbrutus 		    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x011C);
31817169044Sbrutus 
31917169044Sbrutus 		/* check channel error register, clear any errors */
32017169044Sbrutus 		estat = ddi_get32(state->is_reg_handle,
32117169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
32217169044Sbrutus 		if (estat != 0) {
32317169044Sbrutus #ifdef	DEBUG
32417169044Sbrutus 			cmn_err(CE_CONT, "cleared errors (0x%x) before channel"
32517169044Sbrutus 			    " (%d) enable\n", estat, channel->ic_chan_num);
32617169044Sbrutus #endif
32717169044Sbrutus 			ddi_put32(state->is_reg_handle,
32817169044Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR],
32917169044Sbrutus 			    estat);
33017169044Sbrutus 		}
33117169044Sbrutus 
33217169044Sbrutus 		/* Re-initialize the ring */
33317169044Sbrutus 		bzero(ring->cr_desc, channel->ic_desc_alloc_size);
33417169044Sbrutus 		/* write the physical address into the chain address register */
33517169044Sbrutus 		if (channel->ic_ver == IOAT_CBv1) {
33617169044Sbrutus 			ddi_put32(state->is_reg_handle,
33717169044Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO],
33817169044Sbrutus 			    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
33917169044Sbrutus 			ddi_put32(state->is_reg_handle,
34017169044Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI],
34117169044Sbrutus 			    (uint32_t)(ring->cr_phys_desc >> 32));
34217169044Sbrutus 		} else {
34317169044Sbrutus 			ASSERT(channel->ic_ver == IOAT_CBv2);
34417169044Sbrutus 			ddi_put32(state->is_reg_handle,
34517169044Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO],
34617169044Sbrutus 			    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
34717169044Sbrutus 			ddi_put32(state->is_reg_handle,
34817169044Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI],
34917169044Sbrutus 			    (uint32_t)(ring->cr_phys_desc >> 32));
35017169044Sbrutus 		}
35117169044Sbrutus 
35217169044Sbrutus 		/* re-initialize the completion buffer */
35317169044Sbrutus 		bzero((void *)channel->ic_cmpl, channel->ic_cmpl_alloc_size);
35417169044Sbrutus 		/* write the phys addr into the completion address register */
35517169044Sbrutus 		ddi_put32(state->is_reg_handle,
35617169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO],
35717169044Sbrutus 		    (uint32_t)(channel->ic_phys_cmpl & 0xffffffff));
35817169044Sbrutus 		ddi_put32(state->is_reg_handle,
35917169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI],
36017169044Sbrutus 		    (uint32_t)(channel->ic_phys_cmpl >> 32));
36117169044Sbrutus 
36217169044Sbrutus 		/* start-up the channel */
36317169044Sbrutus 		ioat_channel_start(channel);
36417169044Sbrutus 
36517169044Sbrutus 	}
36617169044Sbrutus 
36717169044Sbrutus 	return (DDI_SUCCESS);
36817169044Sbrutus }
36917169044Sbrutus 
37019397407SSherry Moore /*
37119397407SSherry Moore  * quiesce(9E) entry point.
37219397407SSherry Moore  *
37319397407SSherry Moore  * This function is called when the system is single-threaded at high
37419397407SSherry Moore  * PIL with preemption disabled. Therefore, this function must not be
37519397407SSherry Moore  * blocked.
37619397407SSherry Moore  *
37719397407SSherry Moore  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
37819397407SSherry Moore  * DDI_FAILURE indicates an error condition and should almost never happen.
37919397407SSherry Moore  */
38019397407SSherry Moore void
ioat_channel_quiesce(ioat_state_t * state)38119397407SSherry Moore ioat_channel_quiesce(ioat_state_t *state)
38219397407SSherry Moore {
38319397407SSherry Moore 	int i;
38419397407SSherry Moore 
38519397407SSherry Moore 	/*
38619397407SSherry Moore 	 * Walk through all channels and quiesce
38719397407SSherry Moore 	 */
38819397407SSherry Moore 	for (i = 0; i < state->is_num_channels; i++) {
38919397407SSherry Moore 
39019397407SSherry Moore 		ioat_channel_t	channel = state->is_channel + i;
39119397407SSherry Moore 
39219397407SSherry Moore 		if (!channel->ic_inuse)
39319397407SSherry Moore 			continue;
39419397407SSherry Moore 
39519397407SSherry Moore 		/* disable the interrupts */
39619397407SSherry Moore 		ddi_put16(state->is_reg_handle,
39719397407SSherry Moore 		    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL],
39819397407SSherry Moore 		    0x0);
39919397407SSherry Moore 
40019397407SSherry Moore 		ioat_channel_reset(channel);
40119397407SSherry Moore 	}
40219397407SSherry Moore }
40319397407SSherry Moore 
40417169044Sbrutus 
40517169044Sbrutus /*
40617169044Sbrutus  * ioat_channel_free()
40717169044Sbrutus  */
40817169044Sbrutus void
ioat_channel_free(void * channel_private)40917169044Sbrutus ioat_channel_free(void *channel_private)
41017169044Sbrutus {
41117169044Sbrutus 	struct ioat_channel_s *channel;
41217169044Sbrutus 	ioat_channel_t *chan;
41317169044Sbrutus 	ioat_state_t *state;
41417169044Sbrutus 	uint_t chan_num;
41517169044Sbrutus 
41617169044Sbrutus 
41717169044Sbrutus 	chan = (ioat_channel_t *)channel_private;
41817169044Sbrutus 	channel = *chan;
41917169044Sbrutus 
42017169044Sbrutus 	state = channel->ic_state;
42117169044Sbrutus 	chan_num = channel->ic_chan_num;
42217169044Sbrutus 
42317169044Sbrutus 	/* disable the interrupts */
42417169044Sbrutus 	ddi_put16(state->is_reg_handle,
42517169044Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x0);
42617169044Sbrutus 
42717169044Sbrutus 	ioat_channel_reset(channel);
42817169044Sbrutus 
42917169044Sbrutus 	/* cleanup command cache */
43017169044Sbrutus 	kmem_cache_destroy(channel->ic_cmd_cache);
43117169044Sbrutus 
43217169044Sbrutus 	/* clean-up/free-up the completion space and descriptors */
43317169044Sbrutus 	ioat_completion_free(channel);
43417169044Sbrutus 	ioat_ring_free(channel);
43517169044Sbrutus 
43617169044Sbrutus 	channel->ic_inuse = B_FALSE;
43717169044Sbrutus 
43817169044Sbrutus 	/* free the H/W DMA engine */
43917169044Sbrutus 	ioat_rs_free(state->is_channel_rs, chan_num);
44017169044Sbrutus 
44117169044Sbrutus 	*chan = NULL;
44217169044Sbrutus }
44317169044Sbrutus 
44417169044Sbrutus 
44517169044Sbrutus /*
44617169044Sbrutus  * ioat_channel_intr()
44717169044Sbrutus  */
44817169044Sbrutus void
ioat_channel_intr(ioat_channel_t channel)44917169044Sbrutus ioat_channel_intr(ioat_channel_t channel)
45017169044Sbrutus {
45117169044Sbrutus 	ioat_state_t *state;
45217169044Sbrutus 	uint16_t chanctrl;
45317169044Sbrutus 	uint32_t chanerr;
45417169044Sbrutus 	uint32_t status;
45517169044Sbrutus 
45617169044Sbrutus 
45717169044Sbrutus 	state = channel->ic_state;
45817169044Sbrutus 
45917169044Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
46017169044Sbrutus 		status = ddi_get32(state->is_reg_handle,
46117169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_STS_LO]);
46217169044Sbrutus 	} else {
46317169044Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
46417169044Sbrutus 		status = ddi_get32(state->is_reg_handle,
46517169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_STS_LO]);
46617169044Sbrutus 	}
46717169044Sbrutus 
46817169044Sbrutus 	/* if that status isn't ACTIVE or IDLE, the channel has failed */
46917169044Sbrutus 	if (status & IOAT_CHAN_STS_FAIL_MASK) {
47017169044Sbrutus 		chanerr = ddi_get32(state->is_reg_handle,
47117169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
47217169044Sbrutus 		cmn_err(CE_WARN, "channel(%d) fatal failure! "
47317169044Sbrutus 		    "chanstat_lo=0x%X; chanerr=0x%X\n",
47417169044Sbrutus 		    channel->ic_chan_num, status, chanerr);
47517169044Sbrutus 		channel->ic_channel_state = IOAT_CHANNEL_IN_FAILURE;
47617169044Sbrutus 		ioat_channel_reset(channel);
47717169044Sbrutus 
47817169044Sbrutus 		return;
47917169044Sbrutus 	}
48017169044Sbrutus 
48117169044Sbrutus 	/*
48217169044Sbrutus 	 * clear interrupt disable bit if set (it's a RW1C). Read it back to
48317169044Sbrutus 	 * ensure the write completes.
48417169044Sbrutus 	 */
48517169044Sbrutus 	chanctrl = ddi_get16(state->is_reg_handle,
48617169044Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL]);
48717169044Sbrutus 	ddi_put16(state->is_reg_handle,
48817169044Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], chanctrl);
48917169044Sbrutus 	(void) ddi_get16(state->is_reg_handle,
49017169044Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL]);
49117169044Sbrutus 
49217169044Sbrutus 	/* tell dcopy we have seen a completion on this channel */
49317169044Sbrutus 	dcopy_device_channel_notify(channel->ic_dcopy_handle, DCOPY_COMPLETION);
49417169044Sbrutus }
49517169044Sbrutus 
49617169044Sbrutus 
49717169044Sbrutus /*
49817169044Sbrutus  * ioat_channel_start()
49917169044Sbrutus  */
50017169044Sbrutus void
ioat_channel_start(ioat_channel_t channel)50117169044Sbrutus ioat_channel_start(ioat_channel_t channel)
50217169044Sbrutus {
50317169044Sbrutus 	ioat_chan_dma_desc_t desc;
50417169044Sbrutus 
50517169044Sbrutus 	/* set the first descriptor up as a NULL descriptor */
50617169044Sbrutus 	bzero(&desc, sizeof (desc));
50717169044Sbrutus 	desc.dd_size = 0;
50817169044Sbrutus 	desc.dd_ctrl = IOAT_DESC_CTRL_OP_DMA | IOAT_DESC_DMACTRL_NULL |
50917169044Sbrutus 	    IOAT_DESC_CTRL_CMPL;
51017169044Sbrutus 	desc.dd_next_desc = 0x0;
51117169044Sbrutus 
51217169044Sbrutus 	/* setup the very first descriptor */
51317169044Sbrutus 	ioat_ring_seed(channel, &desc);
51417169044Sbrutus }
51517169044Sbrutus 
51617169044Sbrutus 
51717169044Sbrutus /*
51817169044Sbrutus  * ioat_channel_reset()
51917169044Sbrutus  */
52017169044Sbrutus void
ioat_channel_reset(ioat_channel_t channel)52117169044Sbrutus ioat_channel_reset(ioat_channel_t channel)
52217169044Sbrutus {
52317169044Sbrutus 	ioat_state_t *state;
52417169044Sbrutus 
52517169044Sbrutus 	state = channel->ic_state;
52617169044Sbrutus 
52717169044Sbrutus 	/* hit the reset bit */
52817169044Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
52917169044Sbrutus 		ddi_put8(state->is_reg_handle,
53017169044Sbrutus 		    &channel->ic_regs[IOAT_V1_CHAN_CMD], 0x20);
53117169044Sbrutus 	} else {
53217169044Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
53317169044Sbrutus 		ddi_put8(state->is_reg_handle,
53417169044Sbrutus 		    &channel->ic_regs[IOAT_V2_CHAN_CMD], 0x20);
53517169044Sbrutus 	}
53617169044Sbrutus }
53717169044Sbrutus 
53817169044Sbrutus 
53917169044Sbrutus /*
54017169044Sbrutus  * ioat_completion_alloc()
54117169044Sbrutus  */
54217169044Sbrutus int
ioat_completion_alloc(ioat_channel_t channel)54317169044Sbrutus ioat_completion_alloc(ioat_channel_t channel)
54417169044Sbrutus {
54517169044Sbrutus 	ioat_state_t *state;
54617169044Sbrutus 	size_t real_length;
54717169044Sbrutus 	uint_t cookie_cnt;
54817169044Sbrutus 	int e;
54917169044Sbrutus 
55017169044Sbrutus 
55117169044Sbrutus 	state = channel->ic_state;
55217169044Sbrutus 
55317169044Sbrutus 	/*
55417169044Sbrutus 	 * allocate memory for the completion status, zero it out, and get
55517169044Sbrutus 	 * the paddr. We'll allocate a physically contiguous cache line.
55617169044Sbrutus 	 */
55717169044Sbrutus 	e = ddi_dma_alloc_handle(state->is_dip, &ioat_cmpl_dma_attr,
55817169044Sbrutus 	    DDI_DMA_SLEEP, NULL, &channel->ic_cmpl_dma_handle);
55917169044Sbrutus 	if (e != DDI_SUCCESS) {
56017169044Sbrutus 		goto cmplallocfail_alloc_handle;
56117169044Sbrutus 	}
56217169044Sbrutus 	channel->ic_cmpl_alloc_size = 64;
56317169044Sbrutus 	e = ddi_dma_mem_alloc(channel->ic_cmpl_dma_handle,
56417169044Sbrutus 	    channel->ic_cmpl_alloc_size, &ioat_acc_attr,
56517169044Sbrutus 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
56617169044Sbrutus 	    (caddr_t *)&channel->ic_cmpl, &real_length,
56717169044Sbrutus 	    &channel->ic_cmpl_handle);
56817169044Sbrutus 	if (e != DDI_SUCCESS) {
56917169044Sbrutus 		goto cmplallocfail_mem_alloc;
57017169044Sbrutus 	}
57117169044Sbrutus 	bzero((void *)channel->ic_cmpl, channel->ic_cmpl_alloc_size);
57217169044Sbrutus 	e = ddi_dma_addr_bind_handle(channel->ic_cmpl_dma_handle, NULL,
57317169044Sbrutus 	    (caddr_t)channel->ic_cmpl, channel->ic_cmpl_alloc_size,
57417169044Sbrutus 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
57517169044Sbrutus 	    &channel->ic_cmpl_cookie, &cookie_cnt);
57617169044Sbrutus 	if (e != DDI_SUCCESS) {
57717169044Sbrutus 		goto cmplallocfail_addr_bind;
57817169044Sbrutus 	}
57917169044Sbrutus 	ASSERT(cookie_cnt == 1);
58017169044Sbrutus 	ASSERT(channel->ic_cmpl_cookie.dmac_size ==
58117169044Sbrutus 	    channel->ic_cmpl_alloc_size);
58217169044Sbrutus 	channel->ic_phys_cmpl = channel->ic_cmpl_cookie.dmac_laddress;
58317169044Sbrutus 
58417169044Sbrutus 	/* write the physical address into the completion address register */
58517169044Sbrutus 	ddi_put32(state->is_reg_handle,
58617169044Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO],
58717169044Sbrutus 	    (uint32_t)(channel->ic_phys_cmpl & 0xffffffff));
58817169044Sbrutus 	ddi_put32(state->is_reg_handle,
58917169044Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI],
59017169044Sbrutus 	    (uint32_t)(channel->ic_phys_cmpl >> 32));
59117169044Sbrutus 
59217169044Sbrutus 	return (DDI_SUCCESS);
59317169044Sbrutus 
59417169044Sbrutus cmplallocfail_addr_bind:
59517169044Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
59617169044Sbrutus cmplallocfail_mem_alloc:
59717169044Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
59817169044Sbrutus cmplallocfail_alloc_handle:
59917169044Sbrutus 	return (DDI_FAILURE);
60017169044Sbrutus }
60117169044Sbrutus 
60217169044Sbrutus 
60317169044Sbrutus /*
60417169044Sbrutus  * ioat_completion_free()
60517169044Sbrutus  */
60617169044Sbrutus void
ioat_completion_free(ioat_channel_t channel)60717169044Sbrutus ioat_completion_free(ioat_channel_t channel)
60817169044Sbrutus {
60917169044Sbrutus 	ioat_state_t *state;
61017169044Sbrutus 
61117169044Sbrutus 	state = channel->ic_state;
61217169044Sbrutus 
61317169044Sbrutus 	/* reset the completion address register */
61417169044Sbrutus 	ddi_put32(state->is_reg_handle,
61517169044Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO], 0x0);
61617169044Sbrutus 	ddi_put32(state->is_reg_handle,
61717169044Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI], 0x0);
61817169044Sbrutus 
61917169044Sbrutus 	/* unbind, then free up the memory, dma handle */
62017169044Sbrutus 	(void) ddi_dma_unbind_handle(channel->ic_cmpl_dma_handle);
62117169044Sbrutus 	ddi_dma_mem_free(&channel->ic_cmpl_handle);
62217169044Sbrutus 	ddi_dma_free_handle(&channel->ic_cmpl_dma_handle);
62317169044Sbrutus }
62417169044Sbrutus 
62517169044Sbrutus /*
62617169044Sbrutus  * ioat_ring_alloc()
62717169044Sbrutus  */
62817169044Sbrutus int
ioat_ring_alloc(ioat_channel_t channel,uint_t desc_cnt)62917169044Sbrutus ioat_ring_alloc(ioat_channel_t channel, uint_t desc_cnt)
63017169044Sbrutus {
63117169044Sbrutus 	ioat_channel_ring_t *ring;
63217169044Sbrutus 	ioat_state_t *state;
63317169044Sbrutus 	size_t real_length;
63417169044Sbrutus 	uint_t cookie_cnt;
63517169044Sbrutus 	int e;
63617169044Sbrutus 
63717169044Sbrutus 
63817169044Sbrutus 	state = channel->ic_state;
63917169044Sbrutus 
64017169044Sbrutus 	ring = kmem_zalloc(sizeof (ioat_channel_ring_t), KM_SLEEP);
64117169044Sbrutus 	channel->ic_ring = ring;
64217169044Sbrutus 	ring->cr_chan = channel;
64317169044Sbrutus 	ring->cr_post_cnt = 0;
64417169044Sbrutus 
64517169044Sbrutus 	mutex_init(&ring->cr_cmpl_mutex, NULL, MUTEX_DRIVER,
64617169044Sbrutus 	    channel->ic_state->is_iblock_cookie);
64717169044Sbrutus 	mutex_init(&ring->cr_desc_mutex, NULL, MUTEX_DRIVER,
64817169044Sbrutus 	    channel->ic_state->is_iblock_cookie);
64917169044Sbrutus 
65017169044Sbrutus 	/*
65117169044Sbrutus 	 * allocate memory for the ring, zero it out, and get the paddr.
65217169044Sbrutus 	 * We'll allocate a physically contiguous chunck of memory  which
65317169044Sbrutus 	 * simplifies the completion logic.
65417169044Sbrutus 	 */
65517169044Sbrutus 	e = ddi_dma_alloc_handle(state->is_dip, &ioat_desc_dma_attr,
65617169044Sbrutus 	    DDI_DMA_SLEEP, NULL, &channel->ic_desc_dma_handle);
65717169044Sbrutus 	if (e != DDI_SUCCESS) {
65817169044Sbrutus 		goto ringallocfail_alloc_handle;
65917169044Sbrutus 	}
66017169044Sbrutus 	/*
66117169044Sbrutus 	 * allocate one extra descriptor so we can simplify the empty/full
66217169044Sbrutus 	 * logic. Then round that number up to a whole multiple of 4.
66317169044Sbrutus 	 */
66417169044Sbrutus 	channel->ic_chan_desc_cnt = ((desc_cnt + 1) + 3) & ~0x3;
66517169044Sbrutus 	ring->cr_desc_last = channel->ic_chan_desc_cnt - 1;
66617169044Sbrutus 	channel->ic_desc_alloc_size = channel->ic_chan_desc_cnt *
66717169044Sbrutus 	    sizeof (ioat_chan_desc_t);
66817169044Sbrutus 	e = ddi_dma_mem_alloc(channel->ic_desc_dma_handle,
66917169044Sbrutus 	    channel->ic_desc_alloc_size, &ioat_acc_attr,
67017169044Sbrutus 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
67117169044Sbrutus 	    (caddr_t *)&ring->cr_desc, &real_length, &channel->ic_desc_handle);
67217169044Sbrutus 	if (e != DDI_SUCCESS) {
67317169044Sbrutus 		goto ringallocfail_mem_alloc;
67417169044Sbrutus 	}
67517169044Sbrutus 	bzero(ring->cr_desc, channel->ic_desc_alloc_size);
67617169044Sbrutus 	e = ddi_dma_addr_bind_handle(channel->ic_desc_dma_handle, NULL,
67717169044Sbrutus 	    (caddr_t)ring->cr_desc, channel->ic_desc_alloc_size,
67817169044Sbrutus 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
67917169044Sbrutus 	    &channel->ic_desc_cookies, &cookie_cnt);
68017169044Sbrutus 	if (e != DDI_SUCCESS) {
68117169044Sbrutus 		goto ringallocfail_addr_bind;
68217169044Sbrutus 	}
68317169044Sbrutus 	ASSERT(cookie_cnt == 1);
68417169044Sbrutus 	ASSERT(channel->ic_desc_cookies.dmac_size ==
68517169044Sbrutus 	    channel->ic_desc_alloc_size);
68617169044Sbrutus 	ring->cr_phys_desc = channel->ic_desc_cookies.dmac_laddress;
68717169044Sbrutus 
68817169044Sbrutus 	/* write the physical address into the chain address register */
68917169044Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
69017169044Sbrutus 		ddi_put32(state->is_reg_handle,
69117169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO],
69217169044Sbrutus 		    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
69317169044Sbrutus 		ddi_put32(state->is_reg_handle,
69417169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI],
69517169044Sbrutus 		    (uint32_t)(ring->cr_phys_desc >> 32));
69617169044Sbrutus 	} else {
69717169044Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
69817169044Sbrutus 		ddi_put32(state->is_reg_handle,
69917169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO],
70017169044Sbrutus 		    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
70117169044Sbrutus 		ddi_put32(state->is_reg_handle,
70217169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI],
70317169044Sbrutus 		    (uint32_t)(ring->cr_phys_desc >> 32));
70417169044Sbrutus 	}
70517169044Sbrutus 
70617169044Sbrutus 	return (DCOPY_SUCCESS);
70717169044Sbrutus 
70817169044Sbrutus ringallocfail_addr_bind:
70917169044Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
71017169044Sbrutus ringallocfail_mem_alloc:
71117169044Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
71217169044Sbrutus ringallocfail_alloc_handle:
71317169044Sbrutus 	mutex_destroy(&ring->cr_desc_mutex);
71417169044Sbrutus 	mutex_destroy(&ring->cr_cmpl_mutex);
71517169044Sbrutus 	kmem_free(channel->ic_ring, sizeof (ioat_channel_ring_t));
71617169044Sbrutus 
71717169044Sbrutus 	return (DCOPY_FAILURE);
71817169044Sbrutus }
71917169044Sbrutus 
72017169044Sbrutus 
72117169044Sbrutus /*
72217169044Sbrutus  * ioat_ring_free()
72317169044Sbrutus  */
72417169044Sbrutus void
ioat_ring_free(ioat_channel_t channel)72517169044Sbrutus ioat_ring_free(ioat_channel_t channel)
72617169044Sbrutus {
72717169044Sbrutus 	ioat_state_t *state;
72817169044Sbrutus 
72917169044Sbrutus 
73017169044Sbrutus 	state = channel->ic_state;
73117169044Sbrutus 
73217169044Sbrutus 	/* reset the chain address register */
73317169044Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
73417169044Sbrutus 		ddi_put32(state->is_reg_handle,
73517169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO], 0x0);
73617169044Sbrutus 		ddi_put32(state->is_reg_handle,
73717169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI], 0x0);
73817169044Sbrutus 	} else {
73917169044Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
74017169044Sbrutus 		ddi_put32(state->is_reg_handle,
74117169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO], 0x0);
74217169044Sbrutus 		ddi_put32(state->is_reg_handle,
74317169044Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI], 0x0);
74417169044Sbrutus 	}
74517169044Sbrutus 
74617169044Sbrutus 	/* unbind, then free up the memory, dma handle */
74717169044Sbrutus 	(void) ddi_dma_unbind_handle(channel->ic_desc_dma_handle);
74817169044Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
74917169044Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
75017169044Sbrutus 
75117169044Sbrutus 	mutex_destroy(&channel->ic_ring->cr_desc_mutex);
75217169044Sbrutus 	mutex_destroy(&channel->ic_ring->cr_cmpl_mutex);
75317169044Sbrutus 	kmem_free(channel->ic_ring, sizeof (ioat_channel_ring_t));
75417169044Sbrutus 
75517169044Sbrutus }
75617169044Sbrutus 
75717169044Sbrutus 
75817169044Sbrutus /*
75917169044Sbrutus  * ioat_ring_seed()
76017169044Sbrutus  *    write the first descriptor in the ring.
76117169044Sbrutus  */
76217169044Sbrutus void
ioat_ring_seed(ioat_channel_t channel,ioat_chan_dma_desc_t * in_desc)76317169044Sbrutus ioat_ring_seed(ioat_channel_t channel, ioat_chan_dma_desc_t *in_desc)
76417169044Sbrutus {
76517169044Sbrutus 	ioat_channel_ring_t *ring;
76617169044Sbrutus 	ioat_chan_dma_desc_t *desc;
76717169044Sbrutus 	ioat_chan_dma_desc_t *prev;
76817169044Sbrutus 	ioat_state_t *state;
76917169044Sbrutus 
77017169044Sbrutus 
77117169044Sbrutus 	state = channel->ic_state;
77217169044Sbrutus 	ring = channel->ic_ring;
77317169044Sbrutus 
77417169044Sbrutus 	/* init the completion state */
77517169044Sbrutus 	ring->cr_cmpl_gen = 0x0;
77617169044Sbrutus 	ring->cr_cmpl_last = 0x0;
77717169044Sbrutus 
77817169044Sbrutus 	/* write in the descriptor and init the descriptor state */
77917169044Sbrutus 	ring->cr_post_cnt++;
78017169044Sbrutus 	channel->ic_ring->cr_desc[0] = *(ioat_chan_desc_t *)in_desc;
78117169044Sbrutus 	ring->cr_desc_gen = 0;
78217169044Sbrutus 	ring->cr_desc_prev = 0;
78317169044Sbrutus 	ring->cr_desc_next = 1;
78417169044Sbrutus 
78517169044Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
78617169044Sbrutus 		/* hit the start bit */
78717169044Sbrutus 		ddi_put8(state->is_reg_handle,
78817169044Sbrutus 		    &channel->ic_regs[IOAT_V1_CHAN_CMD], 0x1);
78917169044Sbrutus 	} else {
79017169044Sbrutus 		/*
79117169044Sbrutus 		 * if this is CBv2, link the descriptor to an empty
79217169044Sbrutus 		 * descriptor
79317169044Sbrutus 		 */
79417169044Sbrutus 		ASSERT(ring->cr_chan->ic_ver == IOAT_CBv2);
79517169044Sbrutus 		desc = (ioat_chan_dma_desc_t *)
79617169044Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
79717169044Sbrutus 		prev = (ioat_chan_dma_desc_t *)
79817169044Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
79917169044Sbrutus 
80017169044Sbrutus 		desc->dd_ctrl = 0;
80117169044Sbrutus 		desc->dd_next_desc = 0x0;
80217169044Sbrutus 
80317169044Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
80417169044Sbrutus 		    (ring->cr_desc_next << 6);
80517169044Sbrutus 
80617169044Sbrutus 		ddi_put16(state->is_reg_handle,
80717169044Sbrutus 		    (uint16_t *)&channel->ic_regs[IOAT_V2_CHAN_CNT],
80817169044Sbrutus 		    (uint16_t)1);
80917169044Sbrutus 	}
81017169044Sbrutus 
81117169044Sbrutus }
81217169044Sbrutus 
813*eca2601cSRandy Fishel /*
814*eca2601cSRandy Fishel  * ioat_ring_loop()
815*eca2601cSRandy Fishel  * Make the ring loop for CB v1
816*eca2601cSRandy Fishel  * This function assume we are in the ring->cr_desc_mutex mutex context
817*eca2601cSRandy Fishel  */
818*eca2601cSRandy Fishel int
ioat_ring_loop(ioat_channel_ring_t * ring,dcopy_cmd_t cmd)819*eca2601cSRandy Fishel ioat_ring_loop(ioat_channel_ring_t *ring, dcopy_cmd_t cmd)
820*eca2601cSRandy Fishel {
821*eca2601cSRandy Fishel 	uint64_t count;
822*eca2601cSRandy Fishel 	ioat_channel_t channel;
823*eca2601cSRandy Fishel 	ioat_chan_dma_desc_t *curr;
824*eca2601cSRandy Fishel 	ioat_cmd_private_t *prevpriv;
825*eca2601cSRandy Fishel 	ioat_cmd_private_t *currpriv;
826*eca2601cSRandy Fishel 
827*eca2601cSRandy Fishel 	channel = ring->cr_chan;
828*eca2601cSRandy Fishel 	ASSERT(channel->ic_ver == IOAT_CBv1);
829*eca2601cSRandy Fishel 
830*eca2601cSRandy Fishel 	/*
831*eca2601cSRandy Fishel 	 * For each cmd in the command queue, check whether they are continuous
832*eca2601cSRandy Fishel 	 * in descriptor ring. Return error if not continuous.
833*eca2601cSRandy Fishel 	 */
834*eca2601cSRandy Fishel 	for (count = 0, prevpriv = NULL;
835*eca2601cSRandy Fishel 	    cmd != NULL && count <= channel->ic_chan_desc_cnt;
836*eca2601cSRandy Fishel 	    prevpriv = currpriv) {
837*eca2601cSRandy Fishel 		currpriv = cmd->dp_private->pr_device_cmd_private;
838*eca2601cSRandy Fishel 		if (prevpriv != NULL &&
839*eca2601cSRandy Fishel 		    currpriv->ip_index + 1 != prevpriv->ip_start &&
840*eca2601cSRandy Fishel 		    currpriv->ip_index + 1 != prevpriv->ip_start +
841*eca2601cSRandy Fishel 		    channel->ic_chan_desc_cnt) {
842*eca2601cSRandy Fishel 			/* Non-continuous, other commands get interleaved */
843*eca2601cSRandy Fishel 			return (DCOPY_FAILURE);
844*eca2601cSRandy Fishel 		}
845*eca2601cSRandy Fishel 		if (currpriv->ip_index < currpriv->ip_start) {
846*eca2601cSRandy Fishel 			count += channel->ic_chan_desc_cnt
847*eca2601cSRandy Fishel 			    + currpriv->ip_index - currpriv->ip_start + 1;
848*eca2601cSRandy Fishel 		} else {
849*eca2601cSRandy Fishel 			count += currpriv->ip_index - currpriv->ip_start + 1;
850*eca2601cSRandy Fishel 		}
851*eca2601cSRandy Fishel 		cmd = currpriv->ip_next;
852*eca2601cSRandy Fishel 	}
853*eca2601cSRandy Fishel 	/*
854*eca2601cSRandy Fishel 	 * Check for too many descriptors which would cause wrap around in
855*eca2601cSRandy Fishel 	 * descriptor ring. And make sure there is space for cancel operation.
856*eca2601cSRandy Fishel 	 */
857*eca2601cSRandy Fishel 	if (count >= channel->ic_chan_desc_cnt) {
858*eca2601cSRandy Fishel 		return (DCOPY_FAILURE);
859*eca2601cSRandy Fishel 	}
860*eca2601cSRandy Fishel 
861*eca2601cSRandy Fishel 	/* Point next descriptor to header of chain. */
862*eca2601cSRandy Fishel 	curr = (ioat_chan_dma_desc_t *)&ring->cr_desc[ring->cr_desc_prev];
863*eca2601cSRandy Fishel 	curr->dd_next_desc = ring->cr_phys_desc + (currpriv->ip_start << 6);
864*eca2601cSRandy Fishel 
865*eca2601cSRandy Fishel 	/* sync the last desc */
866*eca2601cSRandy Fishel 	(void) ddi_dma_sync(channel->ic_desc_dma_handle,
867*eca2601cSRandy Fishel 	    ring->cr_desc_prev << 6, 64, DDI_DMA_SYNC_FORDEV);
868*eca2601cSRandy Fishel 
869*eca2601cSRandy Fishel 	return (DCOPY_SUCCESS);
870*eca2601cSRandy Fishel }
871*eca2601cSRandy Fishel 
87217169044Sbrutus 
87317169044Sbrutus /*
87417169044Sbrutus  * ioat_cmd_alloc()
87517169044Sbrutus  */
87617169044Sbrutus int
ioat_cmd_alloc(void * private,int flags,dcopy_cmd_t * cmd)87717169044Sbrutus ioat_cmd_alloc(void *private, int flags, dcopy_cmd_t *cmd)
87817169044Sbrutus {
87917169044Sbrutus 	ioat_cmd_private_t *priv;
88017169044Sbrutus 	ioat_channel_t channel;
88117169044Sbrutus 	dcopy_cmd_t oldcmd;
88217169044Sbrutus 	int kmflag;
88317169044Sbrutus 
88417169044Sbrutus 
88517169044Sbrutus 	channel = (ioat_channel_t)private;
88617169044Sbrutus 
88717169044Sbrutus 	if (flags & DCOPY_NOSLEEP) {
88817169044Sbrutus 		kmflag = KM_NOSLEEP;
88917169044Sbrutus 	} else {
89017169044Sbrutus 		kmflag = KM_SLEEP;
89117169044Sbrutus 	}
89217169044Sbrutus 
89317169044Sbrutus 	/* save the command passed incase DCOPY_ALLOC_LINK is set */
89417169044Sbrutus 	oldcmd = *cmd;
89517169044Sbrutus 
89617169044Sbrutus 	*cmd = kmem_cache_alloc(channel->ic_cmd_cache, kmflag);
89717169044Sbrutus 	if (*cmd == NULL) {
89817169044Sbrutus 		return (DCOPY_NORESOURCES);
89917169044Sbrutus 	}
90017169044Sbrutus 
90117169044Sbrutus 	/* setup the dcopy and ioat private state pointers */
90217169044Sbrutus 	(*cmd)->dp_version = DCOPY_CMD_V0;
90317169044Sbrutus 	(*cmd)->dp_cmd = 0;
90417169044Sbrutus 	(*cmd)->dp_private = (struct dcopy_cmd_priv_s *)
90517169044Sbrutus 	    ((uintptr_t)(*cmd) + sizeof (struct dcopy_cmd_s));
90617169044Sbrutus 	(*cmd)->dp_private->pr_device_cmd_private =
90717169044Sbrutus 	    (struct ioat_cmd_private_s *)((uintptr_t)(*cmd)->dp_private +
90817169044Sbrutus 	    sizeof (struct dcopy_cmd_priv_s));
90917169044Sbrutus 
91017169044Sbrutus 	/*
91117169044Sbrutus 	 * if DCOPY_ALLOC_LINK is set, link the old command to the new one
91217169044Sbrutus 	 * just allocated.
91317169044Sbrutus 	 */
91417169044Sbrutus 	priv = (*cmd)->dp_private->pr_device_cmd_private;
91517169044Sbrutus 	if (flags & DCOPY_ALLOC_LINK) {
91617169044Sbrutus 		priv->ip_next = oldcmd;
91717169044Sbrutus 	} else {
91817169044Sbrutus 		priv->ip_next = NULL;
91917169044Sbrutus 	}
92017169044Sbrutus 
92117169044Sbrutus 	return (DCOPY_SUCCESS);
92217169044Sbrutus }
92317169044Sbrutus 
92417169044Sbrutus 
92517169044Sbrutus /*
92617169044Sbrutus  * ioat_cmd_free()
92717169044Sbrutus  */
92817169044Sbrutus void
ioat_cmd_free(void * private,dcopy_cmd_t * cmdp)92917169044Sbrutus ioat_cmd_free(void *private, dcopy_cmd_t *cmdp)
93017169044Sbrutus {
93117169044Sbrutus 	ioat_cmd_private_t *priv;
93217169044Sbrutus 	ioat_channel_t channel;
93317169044Sbrutus 	dcopy_cmd_t next;
93417169044Sbrutus 	dcopy_cmd_t cmd;
93517169044Sbrutus 
93617169044Sbrutus 
93717169044Sbrutus 	channel = (ioat_channel_t)private;
93817169044Sbrutus 	cmd = *(cmdp);
93917169044Sbrutus 
94017169044Sbrutus 	/*
94117169044Sbrutus 	 * free all the commands in the chain (see DCOPY_ALLOC_LINK in
94217169044Sbrutus 	 * ioat_cmd_alloc() for more info).
94317169044Sbrutus 	 */
94417169044Sbrutus 	while (cmd != NULL) {
94517169044Sbrutus 		priv = cmd->dp_private->pr_device_cmd_private;
94617169044Sbrutus 		next = priv->ip_next;
94717169044Sbrutus 		kmem_cache_free(channel->ic_cmd_cache, cmd);
94817169044Sbrutus 		cmd = next;
94917169044Sbrutus 	}
95017169044Sbrutus 	*cmdp = NULL;
95117169044Sbrutus }
95217169044Sbrutus 
95317169044Sbrutus 
95417169044Sbrutus /*
95517169044Sbrutus  * ioat_cmd_post()
95617169044Sbrutus  */
95717169044Sbrutus int
ioat_cmd_post(void * private,dcopy_cmd_t cmd)95817169044Sbrutus ioat_cmd_post(void *private, dcopy_cmd_t cmd)
95917169044Sbrutus {
96017169044Sbrutus 	ioat_channel_ring_t *ring;
96117169044Sbrutus 	ioat_cmd_private_t *priv;
96217169044Sbrutus 	ioat_channel_t channel;
96317169044Sbrutus 	ioat_state_t *state;
96417169044Sbrutus 	uint64_t dest_paddr;
96517169044Sbrutus 	uint64_t src_paddr;
96617169044Sbrutus 	uint64_t dest_addr;
96717169044Sbrutus 	uint32_t dest_size;
96817169044Sbrutus 	uint64_t src_addr;
96917169044Sbrutus 	uint32_t src_size;
97017169044Sbrutus 	size_t xfer_size;
97117169044Sbrutus 	uint32_t ctrl;
97217169044Sbrutus 	size_t size;
97317169044Sbrutus 	int e;
97417169044Sbrutus 
97517169044Sbrutus 
97617169044Sbrutus 	channel = (ioat_channel_t)private;
97717169044Sbrutus 	priv = cmd->dp_private->pr_device_cmd_private;
97817169044Sbrutus 
97917169044Sbrutus 	state = channel->ic_state;
98017169044Sbrutus 	ring = channel->ic_ring;
98117169044Sbrutus 
982*eca2601cSRandy Fishel 	/*
983*eca2601cSRandy Fishel 	 * Special support for DCOPY_CMD_LOOP option, only supported on CBv1.
984*eca2601cSRandy Fishel 	 * DCOPY_CMD_QUEUE should also be set if DCOPY_CMD_LOOP is set.
985*eca2601cSRandy Fishel 	 */
986*eca2601cSRandy Fishel 	if ((cmd->dp_flags & DCOPY_CMD_LOOP) &&
987*eca2601cSRandy Fishel 	    (channel->ic_ver != IOAT_CBv1 ||
988*eca2601cSRandy Fishel 	    (cmd->dp_flags & DCOPY_CMD_QUEUE))) {
989*eca2601cSRandy Fishel 		return (DCOPY_FAILURE);
990*eca2601cSRandy Fishel 	}
991*eca2601cSRandy Fishel 
992*eca2601cSRandy Fishel 	if ((cmd->dp_flags & DCOPY_CMD_NOWAIT) == 0) {
99317169044Sbrutus 		mutex_enter(&ring->cr_desc_mutex);
99417169044Sbrutus 
995*eca2601cSRandy Fishel 	/*
996*eca2601cSRandy Fishel 	 * Try to acquire mutex if NOWAIT flag is set.
997*eca2601cSRandy Fishel 	 * Return failure if failed to acquire mutex.
998*eca2601cSRandy Fishel 	 */
999*eca2601cSRandy Fishel 	} else if (mutex_tryenter(&ring->cr_desc_mutex) == 0) {
1000*eca2601cSRandy Fishel 		return (DCOPY_FAILURE);
1001*eca2601cSRandy Fishel 	}
1002*eca2601cSRandy Fishel 
100317169044Sbrutus 	/* if the channel has had a fatal failure, return failure */
100417169044Sbrutus 	if (channel->ic_channel_state == IOAT_CHANNEL_IN_FAILURE) {
10054f0f65c2SMark Johnson 		mutex_exit(&ring->cr_desc_mutex);
100617169044Sbrutus 		return (DCOPY_FAILURE);
100717169044Sbrutus 	}
100817169044Sbrutus 
100917169044Sbrutus 	/* make sure we have space for the descriptors */
101017169044Sbrutus 	e = ioat_ring_reserve(channel, ring, cmd);
101117169044Sbrutus 	if (e != DCOPY_SUCCESS) {
10124f0f65c2SMark Johnson 		mutex_exit(&ring->cr_desc_mutex);
101317169044Sbrutus 		return (DCOPY_NORESOURCES);
101417169044Sbrutus 	}
101517169044Sbrutus 
101617169044Sbrutus 	/* if we support DCA, and the DCA flag is set, post a DCA desc */
101717169044Sbrutus 	if ((channel->ic_ver == IOAT_CBv2) &&
101817169044Sbrutus 	    (cmd->dp_flags & DCOPY_CMD_DCA)) {
101917169044Sbrutus 		ioat_cmd_post_dca(ring, cmd->dp_dca_id);
102017169044Sbrutus 	}
102117169044Sbrutus 
102217169044Sbrutus 	/*
102317169044Sbrutus 	 * the dma copy may have to be broken up into multiple descriptors
102417169044Sbrutus 	 * since we can't cross a page boundary.
102517169044Sbrutus 	 */
102617169044Sbrutus 	ASSERT(cmd->dp_version == DCOPY_CMD_V0);
102717169044Sbrutus 	ASSERT(cmd->dp_cmd == DCOPY_CMD_COPY);
102817169044Sbrutus 	src_addr = cmd->dp.copy.cc_source;
102917169044Sbrutus 	dest_addr = cmd->dp.copy.cc_dest;
103017169044Sbrutus 	size = cmd->dp.copy.cc_size;
1031*eca2601cSRandy Fishel 	priv->ip_start = ring->cr_desc_next;
103217169044Sbrutus 	while (size > 0) {
103317169044Sbrutus 		src_paddr = pa_to_ma(src_addr);
103417169044Sbrutus 		dest_paddr = pa_to_ma(dest_addr);
103517169044Sbrutus 
103617169044Sbrutus 		/* adjust for any offset into the page */
103717169044Sbrutus 		if ((src_addr & PAGEOFFSET) == 0) {
103817169044Sbrutus 			src_size = PAGESIZE;
103917169044Sbrutus 		} else {
104017169044Sbrutus 			src_size = PAGESIZE - (src_addr & PAGEOFFSET);
104117169044Sbrutus 		}
104217169044Sbrutus 		if ((dest_addr & PAGEOFFSET) == 0) {
104317169044Sbrutus 			dest_size = PAGESIZE;
104417169044Sbrutus 		} else {
104517169044Sbrutus 			dest_size = PAGESIZE - (dest_addr & PAGEOFFSET);
104617169044Sbrutus 		}
104717169044Sbrutus 
104817169044Sbrutus 		/* take the smallest of the three */
104917169044Sbrutus 		xfer_size = MIN(src_size, dest_size);
105017169044Sbrutus 		xfer_size = MIN(xfer_size, size);
105117169044Sbrutus 
105217169044Sbrutus 		/*
105317169044Sbrutus 		 * if this is the last descriptor, and we are supposed to
105417169044Sbrutus 		 * generate a completion, generate a completion. same logic
105517169044Sbrutus 		 * for interrupt.
105617169044Sbrutus 		 */
105717169044Sbrutus 		ctrl = 0;
1058*eca2601cSRandy Fishel 		if (cmd->dp_flags & DCOPY_CMD_NOSRCSNP) {
1059*eca2601cSRandy Fishel 			ctrl |= IOAT_DESC_CTRL_NOSRCSNP;
1060*eca2601cSRandy Fishel 		}
1061*eca2601cSRandy Fishel 		if (cmd->dp_flags & DCOPY_CMD_NODSTSNP) {
1062*eca2601cSRandy Fishel 			ctrl |= IOAT_DESC_CTRL_NODSTSNP;
1063*eca2601cSRandy Fishel 		}
106417169044Sbrutus 		if (xfer_size == size) {
106517169044Sbrutus 			if (!(cmd->dp_flags & DCOPY_CMD_NOSTAT)) {
106617169044Sbrutus 				ctrl |= IOAT_DESC_CTRL_CMPL;
106717169044Sbrutus 			}
106817169044Sbrutus 			if ((cmd->dp_flags & DCOPY_CMD_INTR)) {
106917169044Sbrutus 				ctrl |= IOAT_DESC_CTRL_INTR;
107017169044Sbrutus 			}
107117169044Sbrutus 		}
107217169044Sbrutus 
107317169044Sbrutus 		ioat_cmd_post_copy(ring, src_paddr, dest_paddr, xfer_size,
107417169044Sbrutus 		    ctrl);
107517169044Sbrutus 
107617169044Sbrutus 		/* go to the next page */
107717169044Sbrutus 		src_addr += xfer_size;
107817169044Sbrutus 		dest_addr += xfer_size;
107917169044Sbrutus 		size -= xfer_size;
108017169044Sbrutus 	}
108117169044Sbrutus 
1082*eca2601cSRandy Fishel 	/* save away the state so we can poll on it. */
108317169044Sbrutus 	priv->ip_generation = ring->cr_desc_gen_prev;
108417169044Sbrutus 	priv->ip_index = ring->cr_desc_prev;
108517169044Sbrutus 
108617169044Sbrutus 	/* if queue not defined, tell the DMA engine about it */
108717169044Sbrutus 	if (!(cmd->dp_flags & DCOPY_CMD_QUEUE)) {
1088*eca2601cSRandy Fishel 		/*
1089*eca2601cSRandy Fishel 		 * Link the ring to a loop (currently only for FIPE).
1090*eca2601cSRandy Fishel 		 */
1091*eca2601cSRandy Fishel 		if (cmd->dp_flags & DCOPY_CMD_LOOP) {
1092*eca2601cSRandy Fishel 			e = ioat_ring_loop(ring, cmd);
1093*eca2601cSRandy Fishel 			if (e != DCOPY_SUCCESS) {
1094*eca2601cSRandy Fishel 				mutex_exit(&ring->cr_desc_mutex);
1095*eca2601cSRandy Fishel 				return (DCOPY_FAILURE);
1096*eca2601cSRandy Fishel 			}
1097*eca2601cSRandy Fishel 		}
1098*eca2601cSRandy Fishel 
109917169044Sbrutus 		if (channel->ic_ver == IOAT_CBv1) {
110017169044Sbrutus 			ddi_put8(state->is_reg_handle,
110117169044Sbrutus 			    (uint8_t *)&channel->ic_regs[IOAT_V1_CHAN_CMD],
110217169044Sbrutus 			    0x2);
110317169044Sbrutus 		} else {
110417169044Sbrutus 			ASSERT(channel->ic_ver == IOAT_CBv2);
110517169044Sbrutus 			ddi_put16(state->is_reg_handle,
110617169044Sbrutus 			    (uint16_t *)&channel->ic_regs[IOAT_V2_CHAN_CNT],
110717169044Sbrutus 			    (uint16_t)(ring->cr_post_cnt & 0xFFFF));
110817169044Sbrutus 		}
110917169044Sbrutus 	}
111017169044Sbrutus 
111117169044Sbrutus 	mutex_exit(&ring->cr_desc_mutex);
111217169044Sbrutus 
111317169044Sbrutus 	return (DCOPY_SUCCESS);
111417169044Sbrutus }
111517169044Sbrutus 
111617169044Sbrutus 
111717169044Sbrutus /*
111817169044Sbrutus  * ioat_cmd_post_dca()
111917169044Sbrutus  */
112017169044Sbrutus static void
ioat_cmd_post_dca(ioat_channel_ring_t * ring,uint32_t dca_id)112117169044Sbrutus ioat_cmd_post_dca(ioat_channel_ring_t *ring, uint32_t dca_id)
112217169044Sbrutus {
11235aefaa16SMark Johnson 	ioat_chan_dca_desc_t *saved_prev;
112417169044Sbrutus 	ioat_chan_dca_desc_t *desc;
112517169044Sbrutus 	ioat_chan_dca_desc_t *prev;
112617169044Sbrutus 	ioat_channel_t channel;
11275aefaa16SMark Johnson 	uint64_t next_desc_phys;
11285aefaa16SMark Johnson 	off_t prev_offset;
11295aefaa16SMark Johnson 	off_t next_offset;
113017169044Sbrutus 
113117169044Sbrutus 
113217169044Sbrutus 	channel = ring->cr_chan;
113317169044Sbrutus 	desc = (ioat_chan_dca_desc_t *)&ring->cr_desc[ring->cr_desc_next];
113417169044Sbrutus 	prev = (ioat_chan_dca_desc_t *)&ring->cr_desc[ring->cr_desc_prev];
113517169044Sbrutus 
113617169044Sbrutus 	/* keep track of the number of descs posted for cbv2 */
113717169044Sbrutus 	ring->cr_post_cnt++;
113817169044Sbrutus 
113917169044Sbrutus 	/*
114017169044Sbrutus 	 * post a context change desriptor. If dca has never been used on
114117169044Sbrutus 	 * this channel, or if the id doesn't match the last id used on this
114217169044Sbrutus 	 * channel, set CONTEXT_CHANGE bit and dca id, set dca state to active,
114317169044Sbrutus 	 * and save away the id we're using.
114417169044Sbrutus 	 */
114517169044Sbrutus 	desc->dd_ctrl = IOAT_DESC_CTRL_OP_CNTX;
114617169044Sbrutus 	desc->dd_next_desc = 0x0;
114717169044Sbrutus 	if (!channel->ic_dca_active || (channel->ic_dca_current != dca_id)) {
114817169044Sbrutus 		channel->ic_dca_active = B_TRUE;
114917169044Sbrutus 		channel->ic_dca_current = dca_id;
115017169044Sbrutus 		desc->dd_ctrl |= IOAT_DESC_CTRL_CNTX_CHNG;
115117169044Sbrutus 		desc->dd_cntx = dca_id;
115217169044Sbrutus 	}
115317169044Sbrutus 
11545aefaa16SMark Johnson 	/*
11555aefaa16SMark Johnson 	 * save next desc and prev offset for when we link the two
11565aefaa16SMark Johnson 	 * descriptors together.
11575aefaa16SMark Johnson 	 */
11585aefaa16SMark Johnson 	saved_prev = prev;
11595aefaa16SMark Johnson 	prev_offset = ring->cr_desc_prev << 6;
11605aefaa16SMark Johnson 	next_offset = ring->cr_desc_next << 6;
11615aefaa16SMark Johnson 	next_desc_phys = ring->cr_phys_desc + next_offset;
116217169044Sbrutus 
116317169044Sbrutus 	/* save the current desc_next and desc_last for the completion */
116417169044Sbrutus 	ring->cr_desc_prev = ring->cr_desc_next;
116517169044Sbrutus 	ring->cr_desc_gen_prev = ring->cr_desc_gen;
116617169044Sbrutus 
116717169044Sbrutus 	/* increment next/gen so it points to the next free desc */
116817169044Sbrutus 	ring->cr_desc_next++;
116917169044Sbrutus 	if (ring->cr_desc_next > ring->cr_desc_last) {
117017169044Sbrutus 		ring->cr_desc_next = 0;
117117169044Sbrutus 		ring->cr_desc_gen++;
117217169044Sbrutus 	}
117317169044Sbrutus 
117417169044Sbrutus 	/*
117517169044Sbrutus 	 * if this is CBv2, link the descriptor to an empty descriptor. Since
117617169044Sbrutus 	 * we always leave on desc empty to detect full, this works out.
117717169044Sbrutus 	 */
117817169044Sbrutus 	if (ring->cr_chan->ic_ver == IOAT_CBv2) {
117917169044Sbrutus 		desc = (ioat_chan_dca_desc_t *)
118017169044Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
118117169044Sbrutus 		prev = (ioat_chan_dca_desc_t *)
118217169044Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
118317169044Sbrutus 		desc->dd_ctrl = 0;
118417169044Sbrutus 		desc->dd_next_desc = 0x0;
11855aefaa16SMark Johnson 		(void) ddi_dma_sync(channel->ic_desc_dma_handle,
11865aefaa16SMark Johnson 		    ring->cr_desc_next << 6, 64, DDI_DMA_SYNC_FORDEV);
118717169044Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
118817169044Sbrutus 		    (ring->cr_desc_next << 6);
118917169044Sbrutus 	}
11905aefaa16SMark Johnson 
11915aefaa16SMark Johnson 	/* Put the descriptors physical address in the previous descriptor */
11925aefaa16SMark Johnson 	/*LINTED:E_TRUE_LOGICAL_EXPR*/
11935aefaa16SMark Johnson 	ASSERT(sizeof (ioat_chan_dca_desc_t) == 64);
11945aefaa16SMark Johnson 
11955aefaa16SMark Johnson 	/* sync the current desc */
11965aefaa16SMark Johnson 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, next_offset, 64,
11975aefaa16SMark Johnson 	    DDI_DMA_SYNC_FORDEV);
11985aefaa16SMark Johnson 
11995aefaa16SMark Johnson 	/* update the previous desc and sync it too */
12005aefaa16SMark Johnson 	saved_prev->dd_next_desc = next_desc_phys;
12015aefaa16SMark Johnson 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, prev_offset, 64,
12025aefaa16SMark Johnson 	    DDI_DMA_SYNC_FORDEV);
120317169044Sbrutus }
120417169044Sbrutus 
120517169044Sbrutus 
120617169044Sbrutus /*
120717169044Sbrutus  * ioat_cmd_post_copy()
120817169044Sbrutus  *
120917169044Sbrutus  */
121017169044Sbrutus static void
ioat_cmd_post_copy(ioat_channel_ring_t * ring,uint64_t src_addr,uint64_t dest_addr,uint32_t size,uint32_t ctrl)121117169044Sbrutus ioat_cmd_post_copy(ioat_channel_ring_t *ring, uint64_t src_addr,
121217169044Sbrutus     uint64_t dest_addr, uint32_t size, uint32_t ctrl)
121317169044Sbrutus {
12145aefaa16SMark Johnson 	ioat_chan_dma_desc_t *saved_prev;
121517169044Sbrutus 	ioat_chan_dma_desc_t *desc;
121617169044Sbrutus 	ioat_chan_dma_desc_t *prev;
121717169044Sbrutus 	ioat_channel_t channel;
12185aefaa16SMark Johnson 	uint64_t next_desc_phy;
12195aefaa16SMark Johnson 	off_t prev_offset;
12205aefaa16SMark Johnson 	off_t next_offset;
122117169044Sbrutus 
122217169044Sbrutus 
122317169044Sbrutus 	channel = ring->cr_chan;
122417169044Sbrutus 	desc = (ioat_chan_dma_desc_t *)&ring->cr_desc[ring->cr_desc_next];
122517169044Sbrutus 	prev = (ioat_chan_dma_desc_t *)&ring->cr_desc[ring->cr_desc_prev];
122617169044Sbrutus 
122717169044Sbrutus 	/* keep track of the number of descs posted for cbv2 */
122817169044Sbrutus 	ring->cr_post_cnt++;
122917169044Sbrutus 
123017169044Sbrutus 	/* write in the DMA desc */
123117169044Sbrutus 	desc->dd_ctrl = IOAT_DESC_CTRL_OP_DMA | ctrl;
123217169044Sbrutus 	desc->dd_size = size;
123317169044Sbrutus 	desc->dd_src_paddr = src_addr;
123417169044Sbrutus 	desc->dd_dest_paddr = dest_addr;
123517169044Sbrutus 	desc->dd_next_desc = 0x0;
123617169044Sbrutus 
12375aefaa16SMark Johnson 	/*
12385aefaa16SMark Johnson 	 * save next desc and prev offset for when we link the two
12395aefaa16SMark Johnson 	 * descriptors together.
12405aefaa16SMark Johnson 	 */
12415aefaa16SMark Johnson 	saved_prev = prev;
12425aefaa16SMark Johnson 	prev_offset = ring->cr_desc_prev << 6;
12435aefaa16SMark Johnson 	next_offset = ring->cr_desc_next << 6;
12445aefaa16SMark Johnson 	next_desc_phy = ring->cr_phys_desc + next_offset;
124517169044Sbrutus 
124617169044Sbrutus 	/* increment next/gen so it points to the next free desc */
124717169044Sbrutus 	ring->cr_desc_prev = ring->cr_desc_next;
124817169044Sbrutus 	ring->cr_desc_gen_prev = ring->cr_desc_gen;
124917169044Sbrutus 
125017169044Sbrutus 	/* increment next/gen so it points to the next free desc */
125117169044Sbrutus 	ring->cr_desc_next++;
125217169044Sbrutus 	if (ring->cr_desc_next > ring->cr_desc_last) {
125317169044Sbrutus 		ring->cr_desc_next = 0;
125417169044Sbrutus 		ring->cr_desc_gen++;
125517169044Sbrutus 	}
125617169044Sbrutus 
125717169044Sbrutus 	/*
125817169044Sbrutus 	 * if this is CBv2, link the descriptor to an empty descriptor. Since
125917169044Sbrutus 	 * we always leave on desc empty to detect full, this works out.
126017169044Sbrutus 	 */
126117169044Sbrutus 	if (ring->cr_chan->ic_ver == IOAT_CBv2) {
126217169044Sbrutus 		desc = (ioat_chan_dma_desc_t *)
126317169044Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
126417169044Sbrutus 		prev = (ioat_chan_dma_desc_t *)
126517169044Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
126617169044Sbrutus 		desc->dd_size = 0;
126717169044Sbrutus 		desc->dd_ctrl = 0;
126817169044Sbrutus 		desc->dd_next_desc = 0x0;
12695aefaa16SMark Johnson 		(void) ddi_dma_sync(channel->ic_desc_dma_handle,
12705aefaa16SMark Johnson 		    ring->cr_desc_next << 6, 64, DDI_DMA_SYNC_FORDEV);
127117169044Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
127217169044Sbrutus 		    (ring->cr_desc_next << 6);
127317169044Sbrutus 	}
12745aefaa16SMark Johnson 
12755aefaa16SMark Johnson 	/* Put the descriptors physical address in the previous descriptor */
12765aefaa16SMark Johnson 	/*LINTED:E_TRUE_LOGICAL_EXPR*/
12775aefaa16SMark Johnson 	ASSERT(sizeof (ioat_chan_dma_desc_t) == 64);
12785aefaa16SMark Johnson 
12795aefaa16SMark Johnson 	/* sync the current desc */
12805aefaa16SMark Johnson 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, next_offset, 64,
12815aefaa16SMark Johnson 	    DDI_DMA_SYNC_FORDEV);
12825aefaa16SMark Johnson 
12835aefaa16SMark Johnson 	/* update the previous desc and sync it too */
12845aefaa16SMark Johnson 	saved_prev->dd_next_desc = next_desc_phy;
12855aefaa16SMark Johnson 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, prev_offset, 64,
12865aefaa16SMark Johnson 	    DDI_DMA_SYNC_FORDEV);
128717169044Sbrutus }
128817169044Sbrutus 
128917169044Sbrutus 
129017169044Sbrutus /*
129117169044Sbrutus  * ioat_cmd_poll()
129217169044Sbrutus  */
129317169044Sbrutus int
ioat_cmd_poll(void * private,dcopy_cmd_t cmd)129417169044Sbrutus ioat_cmd_poll(void *private, dcopy_cmd_t cmd)
129517169044Sbrutus {
129617169044Sbrutus 	ioat_channel_ring_t *ring;
129717169044Sbrutus 	ioat_cmd_private_t *priv;
129817169044Sbrutus 	ioat_channel_t channel;
129917169044Sbrutus 	uint64_t generation;
130017169044Sbrutus 	uint64_t last_cmpl;
130117169044Sbrutus 
1302*eca2601cSRandy Fishel 	ASSERT(cmd != NULL);
130317169044Sbrutus 	channel = (ioat_channel_t)private;
130417169044Sbrutus 	priv = cmd->dp_private->pr_device_cmd_private;
130517169044Sbrutus 
130617169044Sbrutus 	ring = channel->ic_ring;
130717169044Sbrutus 	ASSERT(ring != NULL);
130817169044Sbrutus 
1309*eca2601cSRandy Fishel 	if ((cmd->dp_flags & DCOPY_CMD_NOWAIT) == 0) {
131017169044Sbrutus 		mutex_enter(&ring->cr_cmpl_mutex);
131117169044Sbrutus 
1312*eca2601cSRandy Fishel 	/*
1313*eca2601cSRandy Fishel 	 * Try to acquire mutex if NOWAIT flag is set.
1314*eca2601cSRandy Fishel 	 * Return failure if failed to acquire mutex.
1315*eca2601cSRandy Fishel 	 */
1316*eca2601cSRandy Fishel 	} else if (mutex_tryenter(&ring->cr_cmpl_mutex) == 0) {
1317*eca2601cSRandy Fishel 		return (DCOPY_FAILURE);
1318*eca2601cSRandy Fishel 	}
1319*eca2601cSRandy Fishel 
132017169044Sbrutus 	/* if the channel had a fatal failure, fail all polls */
132117169044Sbrutus 	if ((channel->ic_channel_state == IOAT_CHANNEL_IN_FAILURE) ||
132217169044Sbrutus 	    IOAT_CMPL_FAILED(channel)) {
132317169044Sbrutus 		mutex_exit(&ring->cr_cmpl_mutex);
132417169044Sbrutus 		return (DCOPY_FAILURE);
132517169044Sbrutus 	}
132617169044Sbrutus 
132717169044Sbrutus 	/*
132817169044Sbrutus 	 * if the current completion is the same as the last time we read one,
132917169044Sbrutus 	 * post is still pending, nothing further to do. We track completions
133017169044Sbrutus 	 * as indexes into the ring since post uses VAs and the H/W returns
133117169044Sbrutus 	 * PAs. We grab a snapshot of generation and last_cmpl in the mutex.
133217169044Sbrutus 	 */
133317169044Sbrutus 	(void) ddi_dma_sync(channel->ic_cmpl_dma_handle, 0, 0,
133417169044Sbrutus 	    DDI_DMA_SYNC_FORCPU);
133517169044Sbrutus 	last_cmpl = IOAT_CMPL_INDEX(channel);
133617169044Sbrutus 	if (last_cmpl != ring->cr_cmpl_last) {
133717169044Sbrutus 		/*
133817169044Sbrutus 		 * if we wrapped the ring, increment the generation. Store
133917169044Sbrutus 		 * the last cmpl. This logic assumes a physically contiguous
134017169044Sbrutus 		 * ring.
134117169044Sbrutus 		 */
134217169044Sbrutus 		if (last_cmpl < ring->cr_cmpl_last) {
134317169044Sbrutus 			ring->cr_cmpl_gen++;
134417169044Sbrutus 		}
134517169044Sbrutus 		ring->cr_cmpl_last = last_cmpl;
134617169044Sbrutus 		generation = ring->cr_cmpl_gen;
134717169044Sbrutus 
134817169044Sbrutus 	} else {
134917169044Sbrutus 		generation = ring->cr_cmpl_gen;
135017169044Sbrutus 	}
135117169044Sbrutus 
135217169044Sbrutus 	mutex_exit(&ring->cr_cmpl_mutex);
135317169044Sbrutus 
135417169044Sbrutus 	/*
135517169044Sbrutus 	 * if cmd isn't passed in, well return.  Useful for updating the
135617169044Sbrutus 	 * consumer pointer (ring->cr_cmpl_last).
135717169044Sbrutus 	 */
1358*eca2601cSRandy Fishel 	if (cmd->dp_flags & DCOPY_CMD_SYNC) {
135917169044Sbrutus 		return (DCOPY_PENDING);
136017169044Sbrutus 	}
136117169044Sbrutus 
136217169044Sbrutus 	/*
136317169044Sbrutus 	 * if the post's generation is old, this post has completed. No reason
136417169044Sbrutus 	 * to go check the last completion. if the generation is the same
136517169044Sbrutus 	 * and if the post is before or = to the last completion processed,
136617169044Sbrutus 	 * the post has completed.
136717169044Sbrutus 	 */
136817169044Sbrutus 	if (priv->ip_generation < generation) {
136917169044Sbrutus 		return (DCOPY_COMPLETED);
137017169044Sbrutus 	} else if ((priv->ip_generation == generation) &&
137117169044Sbrutus 	    (priv->ip_index <= last_cmpl)) {
137217169044Sbrutus 		return (DCOPY_COMPLETED);
137317169044Sbrutus 	}
137417169044Sbrutus 
137517169044Sbrutus 	return (DCOPY_PENDING);
137617169044Sbrutus }
137717169044Sbrutus 
137817169044Sbrutus 
137917169044Sbrutus /*
138017169044Sbrutus  * ioat_ring_reserve()
138117169044Sbrutus  */
138217169044Sbrutus int
ioat_ring_reserve(ioat_channel_t channel,ioat_channel_ring_t * ring,dcopy_cmd_t cmd)138317169044Sbrutus ioat_ring_reserve(ioat_channel_t channel, ioat_channel_ring_t *ring,
138417169044Sbrutus     dcopy_cmd_t cmd)
138517169044Sbrutus {
138617169044Sbrutus 	uint64_t dest_addr;
138717169044Sbrutus 	uint32_t dest_size;
138817169044Sbrutus 	uint64_t src_addr;
138917169044Sbrutus 	uint32_t src_size;
139017169044Sbrutus 	size_t xfer_size;
139117169044Sbrutus 	uint64_t desc;
139217169044Sbrutus 	int num_desc;
139317169044Sbrutus 	size_t size;
139417169044Sbrutus 	int i;
139517169044Sbrutus 
139617169044Sbrutus 
139717169044Sbrutus 	/*
139817169044Sbrutus 	 * figure out how many descriptors we need. This can include a dca
139917169044Sbrutus 	 * desc and multiple desc for a dma copy.
140017169044Sbrutus 	 */
140117169044Sbrutus 	num_desc = 0;
140217169044Sbrutus 	if ((channel->ic_ver == IOAT_CBv2) &&
140317169044Sbrutus 	    (cmd->dp_flags & DCOPY_CMD_DCA)) {
140417169044Sbrutus 		num_desc++;
140517169044Sbrutus 	}
140617169044Sbrutus 	src_addr = cmd->dp.copy.cc_source;
140717169044Sbrutus 	dest_addr = cmd->dp.copy.cc_dest;
140817169044Sbrutus 	size = cmd->dp.copy.cc_size;
140917169044Sbrutus 	while (size > 0) {
141017169044Sbrutus 		num_desc++;
141117169044Sbrutus 
141217169044Sbrutus 		/* adjust for any offset into the page */
141317169044Sbrutus 		if ((src_addr & PAGEOFFSET) == 0) {
141417169044Sbrutus 			src_size = PAGESIZE;
141517169044Sbrutus 		} else {
141617169044Sbrutus 			src_size = PAGESIZE - (src_addr & PAGEOFFSET);
141717169044Sbrutus 		}
141817169044Sbrutus 		if ((dest_addr & PAGEOFFSET) == 0) {
141917169044Sbrutus 			dest_size = PAGESIZE;
142017169044Sbrutus 		} else {
142117169044Sbrutus 			dest_size = PAGESIZE - (dest_addr & PAGEOFFSET);
142217169044Sbrutus 		}
142317169044Sbrutus 
142417169044Sbrutus 		/* take the smallest of the three */
142517169044Sbrutus 		xfer_size = MIN(src_size, dest_size);
142617169044Sbrutus 		xfer_size = MIN(xfer_size, size);
142717169044Sbrutus 
142817169044Sbrutus 		/* go to the next page */
142917169044Sbrutus 		src_addr += xfer_size;
143017169044Sbrutus 		dest_addr += xfer_size;
143117169044Sbrutus 		size -= xfer_size;
143217169044Sbrutus 	}
143317169044Sbrutus 
143417169044Sbrutus 	/* Make sure we have space for these descriptors */
143517169044Sbrutus 	desc = ring->cr_desc_next;
143617169044Sbrutus 	for (i = 0; i < num_desc; i++) {
143717169044Sbrutus 
143817169044Sbrutus 		/*
143917169044Sbrutus 		 * if this is the last descriptor in the ring, see if the
144017169044Sbrutus 		 * last completed descriptor is #0.
144117169044Sbrutus 		 */
144217169044Sbrutus 		if (desc == ring->cr_desc_last) {
144317169044Sbrutus 			if (ring->cr_cmpl_last == 0) {
144417169044Sbrutus 				/*
144517169044Sbrutus 				 * if we think the ring is full, update where
144617169044Sbrutus 				 * the H/W really is and check for full again.
144717169044Sbrutus 				 */
1448*eca2601cSRandy Fishel 				cmd->dp_flags |= DCOPY_CMD_SYNC;
1449*eca2601cSRandy Fishel 				(void) ioat_cmd_poll(channel, cmd);
1450*eca2601cSRandy Fishel 				cmd->dp_flags &= ~DCOPY_CMD_SYNC;
145117169044Sbrutus 				if (ring->cr_cmpl_last == 0) {
145217169044Sbrutus 					return (DCOPY_NORESOURCES);
145317169044Sbrutus 				}
145417169044Sbrutus 			}
145517169044Sbrutus 
145617169044Sbrutus 			/*
145717169044Sbrutus 			 * go to the next descriptor which is zero in this
145817169044Sbrutus 			 * case.
145917169044Sbrutus 			 */
146017169044Sbrutus 			desc = 0;
146117169044Sbrutus 
146217169044Sbrutus 		/*
146317169044Sbrutus 		 * if this is not the last descriptor in the ring, see if
146417169044Sbrutus 		 * the last completion we saw was the next descriptor.
146517169044Sbrutus 		 */
146617169044Sbrutus 		} else {
146717169044Sbrutus 			if ((desc + 1) == ring->cr_cmpl_last) {
146817169044Sbrutus 				/*
146917169044Sbrutus 				 * if we think the ring is full, update where
147017169044Sbrutus 				 * the H/W really is and check for full again.
147117169044Sbrutus 				 */
1472*eca2601cSRandy Fishel 				cmd->dp_flags |= DCOPY_CMD_SYNC;
1473*eca2601cSRandy Fishel 				(void) ioat_cmd_poll(channel, cmd);
1474*eca2601cSRandy Fishel 				cmd->dp_flags &= ~DCOPY_CMD_SYNC;
147517169044Sbrutus 				if ((desc + 1) == ring->cr_cmpl_last) {
147617169044Sbrutus 					return (DCOPY_NORESOURCES);
147717169044Sbrutus 				}
147817169044Sbrutus 			}
147917169044Sbrutus 
148017169044Sbrutus 			/* go to the next descriptor */
148117169044Sbrutus 			desc++;
148217169044Sbrutus 		}
148317169044Sbrutus 	}
148417169044Sbrutus 
148517169044Sbrutus 	return (DCOPY_SUCCESS);
148617169044Sbrutus }
1487