xref: /illumos-gate/usr/src/uts/i86pc/io/ioat/ioat_chan.c (revision 584b574a)
18e50dcc9Sbrutus /*
28e50dcc9Sbrutus  * CDDL HEADER START
38e50dcc9Sbrutus  *
48e50dcc9Sbrutus  * The contents of this file are subject to the terms of the
58e50dcc9Sbrutus  * Common Development and Distribution License (the "License").
68e50dcc9Sbrutus  * You may not use this file except in compliance with the License.
78e50dcc9Sbrutus  *
88e50dcc9Sbrutus  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98e50dcc9Sbrutus  * or http://www.opensolaris.org/os/licensing.
108e50dcc9Sbrutus  * See the License for the specific language governing permissions
118e50dcc9Sbrutus  * and limitations under the License.
128e50dcc9Sbrutus  *
138e50dcc9Sbrutus  * When distributing Covered Code, include this CDDL HEADER in each
148e50dcc9Sbrutus  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158e50dcc9Sbrutus  * If applicable, add the following below this CDDL HEADER, with the
168e50dcc9Sbrutus  * fields enclosed by brackets "[]" replaced with your own identifying
178e50dcc9Sbrutus  * information: Portions Copyright [yyyy] [name of copyright owner]
188e50dcc9Sbrutus  *
198e50dcc9Sbrutus  * CDDL HEADER END
208e50dcc9Sbrutus  */
218e50dcc9Sbrutus 
228e50dcc9Sbrutus /*
234f0f65c2SMark Johnson  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
248e50dcc9Sbrutus  * Use is subject to license terms.
258e50dcc9Sbrutus  */
268e50dcc9Sbrutus 
27eca2601cSRandy Fishel /*
28eca2601cSRandy Fishel  * Copyright (c) 2009, Intel Corporation.
29eca2601cSRandy Fishel  * All rights reserved.
30eca2601cSRandy Fishel  */
31eca2601cSRandy Fishel 
328e50dcc9Sbrutus #include <sys/errno.h>
338e50dcc9Sbrutus #include <sys/types.h>
348e50dcc9Sbrutus #include <sys/conf.h>
358e50dcc9Sbrutus #include <sys/kmem.h>
368e50dcc9Sbrutus #include <sys/ddi.h>
378e50dcc9Sbrutus #include <sys/stat.h>
388e50dcc9Sbrutus #include <sys/sunddi.h>
398e50dcc9Sbrutus #include <sys/file.h>
408e50dcc9Sbrutus #include <sys/open.h>
418e50dcc9Sbrutus #include <sys/modctl.h>
428e50dcc9Sbrutus #include <sys/ddi_impldefs.h>
438e50dcc9Sbrutus #include <sys/sysmacros.h>
448e50dcc9Sbrutus #include <vm/hat.h>
458e50dcc9Sbrutus #include <vm/as.h>
468e50dcc9Sbrutus #include <sys/mach_mmu.h>
478e50dcc9Sbrutus #ifdef __xpv
488e50dcc9Sbrutus #include <sys/hypervisor.h>
498e50dcc9Sbrutus #endif
508e50dcc9Sbrutus 
518e50dcc9Sbrutus #include <sys/ioat.h>
528e50dcc9Sbrutus 
538e50dcc9Sbrutus 
548e50dcc9Sbrutus extern ddi_device_acc_attr_t ioat_acc_attr;
558e50dcc9Sbrutus 
568e50dcc9Sbrutus /* dma attr for the descriptor rings */
578e50dcc9Sbrutus ddi_dma_attr_t ioat_desc_dma_attr = {
588e50dcc9Sbrutus 	DMA_ATTR_V0,		/* dma_attr_version */
598e50dcc9Sbrutus 	0x0,			/* dma_attr_addr_lo */
608e50dcc9Sbrutus 	0xffffffffffffffff,	/* dma_attr_addr_hi */
618e50dcc9Sbrutus 	0xffffffff,		/* dma_attr_count_max */
628e50dcc9Sbrutus 	0x1000,			/* dma_attr_align */
638e50dcc9Sbrutus 	0x1,			/* dma_attr_burstsizes */
648e50dcc9Sbrutus 	0x1,			/* dma_attr_minxfer */
658e50dcc9Sbrutus 	0xffffffff,		/* dma_attr_maxxfer */
668e50dcc9Sbrutus 	0xffffffff,		/* dma_attr_seg */
678e50dcc9Sbrutus 	0x1,			/* dma_attr_sgllen */
688e50dcc9Sbrutus 	0x1,			/* dma_attr_granular */
698e50dcc9Sbrutus 	0x0,			/* dma_attr_flags */
708e50dcc9Sbrutus };
718e50dcc9Sbrutus 
728e50dcc9Sbrutus /* dma attr for the completion buffers */
738e50dcc9Sbrutus ddi_dma_attr_t ioat_cmpl_dma_attr = {
748e50dcc9Sbrutus 	DMA_ATTR_V0,		/* dma_attr_version */
758e50dcc9Sbrutus 	0x0,			/* dma_attr_addr_lo */
768e50dcc9Sbrutus 	0xffffffffffffffff,	/* dma_attr_addr_hi */
778e50dcc9Sbrutus 	0xffffffff,		/* dma_attr_count_max */
788e50dcc9Sbrutus 	0x40,			/* dma_attr_align */
798e50dcc9Sbrutus 	0x1,			/* dma_attr_burstsizes */
808e50dcc9Sbrutus 	0x1,			/* dma_attr_minxfer */
818e50dcc9Sbrutus 	0xffffffff,		/* dma_attr_maxxfer */
828e50dcc9Sbrutus 	0xffffffff,		/* dma_attr_seg */
838e50dcc9Sbrutus 	0x1,			/* dma_attr_sgllen */
848e50dcc9Sbrutus 	0x1,			/* dma_attr_granular */
858e50dcc9Sbrutus 	0x0,			/* dma_attr_flags */
868e50dcc9Sbrutus };
878e50dcc9Sbrutus 
888e50dcc9Sbrutus static int ioat_completion_alloc(ioat_channel_t channel);
898e50dcc9Sbrutus static void ioat_completion_free(ioat_channel_t channel);
908e50dcc9Sbrutus static void ioat_channel_start(ioat_channel_t channel);
918e50dcc9Sbrutus static void ioat_channel_reset(ioat_channel_t channel);
928e50dcc9Sbrutus 
938e50dcc9Sbrutus int ioat_ring_alloc(ioat_channel_t channel, uint_t desc_cnt);
948e50dcc9Sbrutus void ioat_ring_free(ioat_channel_t channel);
958e50dcc9Sbrutus void ioat_ring_seed(ioat_channel_t channel, ioat_chan_dma_desc_t *desc);
968e50dcc9Sbrutus int ioat_ring_reserve(ioat_channel_t channel, ioat_channel_ring_t *ring,
978e50dcc9Sbrutus     dcopy_cmd_t cmd);
988e50dcc9Sbrutus 
998e50dcc9Sbrutus static void ioat_cmd_post_copy(ioat_channel_ring_t *ring, uint64_t src_addr,
1008e50dcc9Sbrutus     uint64_t dest_addr, uint32_t size, uint32_t ctrl);
1018e50dcc9Sbrutus static void ioat_cmd_post_dca(ioat_channel_ring_t *ring, uint32_t dca_id);
1028e50dcc9Sbrutus 
1038e50dcc9Sbrutus 
1048e50dcc9Sbrutus /*
1058e50dcc9Sbrutus  * ioat_channel_init()
1068e50dcc9Sbrutus  */
1078e50dcc9Sbrutus int
ioat_channel_init(ioat_state_t * state)1088e50dcc9Sbrutus ioat_channel_init(ioat_state_t *state)
1098e50dcc9Sbrutus {
1108e50dcc9Sbrutus 	int i;
1118e50dcc9Sbrutus 
1128e50dcc9Sbrutus 	/*
1138e50dcc9Sbrutus 	 * initialize each dma channel's state which doesn't change across
1148e50dcc9Sbrutus 	 * channel alloc/free.
1158e50dcc9Sbrutus 	 */
1168e50dcc9Sbrutus 	state->is_chansize = sizeof (struct ioat_channel_s) *
1178e50dcc9Sbrutus 	    state->is_num_channels;
1188e50dcc9Sbrutus 	state->is_channel = kmem_zalloc(state->is_chansize, KM_SLEEP);
1198e50dcc9Sbrutus 	for (i = 0; i < state->is_num_channels; i++) {
1208e50dcc9Sbrutus 		state->is_channel[i].ic_state = state;
1218e50dcc9Sbrutus 		state->is_channel[i].ic_regs = (uint8_t *)
1228e50dcc9Sbrutus 		    ((uintptr_t)state->is_genregs +
1238e50dcc9Sbrutus 		    (uintptr_t)(IOAT_CHANNELREG_OFFSET * (i + 1)));
1248e50dcc9Sbrutus 	}
1258e50dcc9Sbrutus 
1268e50dcc9Sbrutus 	/* initial the allocator (from 0 to state->is_num_channels) */
1278e50dcc9Sbrutus 	ioat_rs_init(state, 0, state->is_num_channels, &state->is_channel_rs);
1288e50dcc9Sbrutus 
1298e50dcc9Sbrutus 	return (DDI_SUCCESS);
1308e50dcc9Sbrutus }
1318e50dcc9Sbrutus 
1328e50dcc9Sbrutus 
1338e50dcc9Sbrutus /*
1348e50dcc9Sbrutus  * ioat_channel_fini()
1358e50dcc9Sbrutus  */
1368e50dcc9Sbrutus void
ioat_channel_fini(ioat_state_t * state)1378e50dcc9Sbrutus ioat_channel_fini(ioat_state_t *state)
1388e50dcc9Sbrutus {
1398e50dcc9Sbrutus 	ioat_rs_fini(&state->is_channel_rs);
1408e50dcc9Sbrutus 	kmem_free(state->is_channel, state->is_chansize);
1418e50dcc9Sbrutus }
1428e50dcc9Sbrutus 
1438e50dcc9Sbrutus 
1448e50dcc9Sbrutus /*
1458e50dcc9Sbrutus  * ioat_channel_alloc()
1468e50dcc9Sbrutus  *   NOTE: We intentionaly don't handle DCOPY_SLEEP (if no channels are
1478e50dcc9Sbrutus  *	available)
1488e50dcc9Sbrutus  */
1498e50dcc9Sbrutus /*ARGSUSED*/
1508e50dcc9Sbrutus int
ioat_channel_alloc(void * device_private,dcopy_handle_t handle,int flags,uint_t size,dcopy_query_channel_t * info,void * channel_private)1518e50dcc9Sbrutus ioat_channel_alloc(void *device_private, dcopy_handle_t handle, int flags,
1528e50dcc9Sbrutus     uint_t size, dcopy_query_channel_t *info, void *channel_private)
1538e50dcc9Sbrutus {
1548e50dcc9Sbrutus #define	CHANSTRSIZE	20
1558e50dcc9Sbrutus 	struct ioat_channel_s *channel;
1568e50dcc9Sbrutus 	char chanstr[CHANSTRSIZE];
1578e50dcc9Sbrutus 	ioat_channel_t *chan;
1588e50dcc9Sbrutus 	ioat_state_t *state;
1598e50dcc9Sbrutus 	size_t cmd_size;
1608e50dcc9Sbrutus 	uint_t chan_num;
1618e50dcc9Sbrutus 	uint32_t estat;
1628e50dcc9Sbrutus 	int e;
1638e50dcc9Sbrutus 
1648e50dcc9Sbrutus 
1658e50dcc9Sbrutus 	state = (ioat_state_t *)device_private;
1668e50dcc9Sbrutus 	chan = (ioat_channel_t *)channel_private;
1678e50dcc9Sbrutus 
1688e50dcc9Sbrutus 	/* allocate a H/W channel */
1698e50dcc9Sbrutus 	e = ioat_rs_alloc(state->is_channel_rs, &chan_num);
1708e50dcc9Sbrutus 	if (e != DDI_SUCCESS) {
1718e50dcc9Sbrutus 		return (DCOPY_NORESOURCES);
1728e50dcc9Sbrutus 	}
1738e50dcc9Sbrutus 
1748e50dcc9Sbrutus 	channel = &state->is_channel[chan_num];
1758e50dcc9Sbrutus 	channel->ic_inuse = B_TRUE;
1768e50dcc9Sbrutus 	channel->ic_chan_num = chan_num;
1778e50dcc9Sbrutus 	channel->ic_ver = state->is_ver;
1788e50dcc9Sbrutus 	channel->ic_dca_active = B_FALSE;
1798e50dcc9Sbrutus 	channel->ic_channel_state = IOAT_CHANNEL_OK;
1808e50dcc9Sbrutus 	channel->ic_dcopy_handle = handle;
1818e50dcc9Sbrutus 
1828e50dcc9Sbrutus #ifdef	DEBUG
1838e50dcc9Sbrutus 	{
1848e50dcc9Sbrutus 		/* if we're cbv2, verify that the V2 compatibility bit is set */
1858e50dcc9Sbrutus 		uint16_t reg;
1868e50dcc9Sbrutus 		if (channel->ic_ver == IOAT_CBv2) {
1878e50dcc9Sbrutus 			reg = ddi_get16(state->is_reg_handle,
1888e50dcc9Sbrutus 			    (uint16_t *)&channel->ic_regs[IOAT_CHAN_COMP]);
1898e50dcc9Sbrutus 			ASSERT(reg & 0x2);
1908e50dcc9Sbrutus 		}
1918e50dcc9Sbrutus 	}
1928e50dcc9Sbrutus #endif
1938e50dcc9Sbrutus 
1948e50dcc9Sbrutus 	/*
1958e50dcc9Sbrutus 	 * Configure DMA channel
1968e50dcc9Sbrutus 	 *   Channel In Use
1978e50dcc9Sbrutus 	 *   Error Interrupt Enable
1988e50dcc9Sbrutus 	 *   Any Error Abort Enable
1998e50dcc9Sbrutus 	 *   Error Completion Enable
2008e50dcc9Sbrutus 	 */
2018e50dcc9Sbrutus 	ddi_put16(state->is_reg_handle,
2028e50dcc9Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x011C);
2038e50dcc9Sbrutus 
2048e50dcc9Sbrutus 	/* check channel error register, clear any errors */
2058e50dcc9Sbrutus 	estat = ddi_get32(state->is_reg_handle,
2068e50dcc9Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
2078e50dcc9Sbrutus 	if (estat != 0) {
2088e50dcc9Sbrutus #ifdef	DEBUG
2098e50dcc9Sbrutus 		cmn_err(CE_CONT, "cleared errors (0x%x) before channel (%d) "
2108e50dcc9Sbrutus 		    "enable\n", estat, channel->ic_chan_num);
2118e50dcc9Sbrutus #endif
2128e50dcc9Sbrutus 		ddi_put32(state->is_reg_handle,
2138e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR], estat);
2148e50dcc9Sbrutus 	}
2158e50dcc9Sbrutus 
2168e50dcc9Sbrutus 	/* allocate and initialize the descriptor buf */
2178e50dcc9Sbrutus 	e = ioat_ring_alloc(channel, size);
2188e50dcc9Sbrutus 	if (e != DDI_SUCCESS) {
2198e50dcc9Sbrutus 		goto chinitfail_desc_alloc;
2208e50dcc9Sbrutus 	}
2218e50dcc9Sbrutus 
2228e50dcc9Sbrutus 	/* allocate and initialize the completion space */
2238e50dcc9Sbrutus 	e = ioat_completion_alloc(channel);
2248e50dcc9Sbrutus 	if (e != DDI_SUCCESS) {
2258e50dcc9Sbrutus 		goto chinitfail_completion_alloc;
2268e50dcc9Sbrutus 	}
2278e50dcc9Sbrutus 
2288e50dcc9Sbrutus 	/* setup kmem_cache for commands */
2298e50dcc9Sbrutus 	cmd_size = sizeof (struct dcopy_cmd_s) +
2308e50dcc9Sbrutus 	    sizeof (struct dcopy_cmd_priv_s) +
2318e50dcc9Sbrutus 	    sizeof (struct ioat_cmd_private_s);
2328e50dcc9Sbrutus 	(void) snprintf(chanstr, CHANSTRSIZE, "ioat%dchan%dcmd",
2338e50dcc9Sbrutus 	    state->is_instance, channel->ic_chan_num);
2348e50dcc9Sbrutus 	channel->ic_cmd_cache = kmem_cache_create(chanstr, cmd_size, 64,
2358e50dcc9Sbrutus 	    NULL, NULL, NULL, NULL, NULL, 0);
2368e50dcc9Sbrutus 	if (channel->ic_cmd_cache == NULL) {
2378e50dcc9Sbrutus 		goto chinitfail_kmem_cache;
2388e50dcc9Sbrutus 	}
2398e50dcc9Sbrutus 
2408e50dcc9Sbrutus 	/* start-up the channel */
2418e50dcc9Sbrutus 	ioat_channel_start(channel);
2428e50dcc9Sbrutus 
2438e50dcc9Sbrutus 	/* fill in the channel info returned to dcopy */
2448e50dcc9Sbrutus 	info->qc_version = DCOPY_QUERY_CHANNEL_V0;
2458e50dcc9Sbrutus 	info->qc_id = state->is_deviceinfo.di_id;
2468e50dcc9Sbrutus 	info->qc_capabilities = (uint64_t)state->is_capabilities;
2478e50dcc9Sbrutus 	info->qc_channel_size = (uint64_t)size;
2488e50dcc9Sbrutus 	info->qc_chan_num = (uint64_t)channel->ic_chan_num;
2498e50dcc9Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
2508e50dcc9Sbrutus 		info->qc_dca_supported = B_FALSE;
2518e50dcc9Sbrutus 	} else {
2528e50dcc9Sbrutus 		if (info->qc_capabilities & IOAT_DMACAP_DCA) {
2538e50dcc9Sbrutus 			info->qc_dca_supported = B_TRUE;
2548e50dcc9Sbrutus 		} else {
2558e50dcc9Sbrutus 			info->qc_dca_supported = B_FALSE;
2568e50dcc9Sbrutus 		}
2578e50dcc9Sbrutus 	}
2588e50dcc9Sbrutus 
2598e50dcc9Sbrutus 	*chan = channel;
2608e50dcc9Sbrutus 
2618e50dcc9Sbrutus 	return (DCOPY_SUCCESS);
2628e50dcc9Sbrutus 
2638e50dcc9Sbrutus chinitfail_kmem_cache:
2648e50dcc9Sbrutus 	ioat_completion_free(channel);
2658e50dcc9Sbrutus chinitfail_completion_alloc:
2668e50dcc9Sbrutus 	ioat_ring_free(channel);
2678e50dcc9Sbrutus chinitfail_desc_alloc:
2688e50dcc9Sbrutus 	return (DCOPY_FAILURE);
2698e50dcc9Sbrutus }
2708e50dcc9Sbrutus 
2718e50dcc9Sbrutus 
2728e50dcc9Sbrutus /*
2738e50dcc9Sbrutus  * ioat_channel_suspend()
2748e50dcc9Sbrutus  */
2758e50dcc9Sbrutus /*ARGSUSED*/
2768e50dcc9Sbrutus void
ioat_channel_suspend(ioat_state_t * state)2778e50dcc9Sbrutus ioat_channel_suspend(ioat_state_t *state)
2788e50dcc9Sbrutus {
2798e50dcc9Sbrutus 	/*
2808e50dcc9Sbrutus 	 * normally you would disable interrupts and reset the H/W here. But
2818e50dcc9Sbrutus 	 * since the suspend framework doesn't know who is using us, it may
2828e50dcc9Sbrutus 	 * not suspend their I/O before us.  Since we won't actively be doing
2838e50dcc9Sbrutus 	 * any DMA or interrupts unless someone asks us to, it's safe to not
2848e50dcc9Sbrutus 	 * do anything here.
2858e50dcc9Sbrutus 	 */
2868e50dcc9Sbrutus }
2878e50dcc9Sbrutus 
2888e50dcc9Sbrutus 
2898e50dcc9Sbrutus /*
2908e50dcc9Sbrutus  * ioat_channel_resume()
2918e50dcc9Sbrutus  */
2928e50dcc9Sbrutus int
ioat_channel_resume(ioat_state_t * state)2938e50dcc9Sbrutus ioat_channel_resume(ioat_state_t *state)
2948e50dcc9Sbrutus {
2958e50dcc9Sbrutus 	ioat_channel_ring_t *ring;
2968e50dcc9Sbrutus 	ioat_channel_t channel;
2978e50dcc9Sbrutus 	uint32_t estat;
2988e50dcc9Sbrutus 	int i;
2998e50dcc9Sbrutus 
3008e50dcc9Sbrutus 
3018e50dcc9Sbrutus 	for (i = 0; i < state->is_num_channels; i++) {
3028e50dcc9Sbrutus 		channel = &state->is_channel[i];
3038e50dcc9Sbrutus 		ring = channel->ic_ring;
3048e50dcc9Sbrutus 
3058e50dcc9Sbrutus 		if (!channel->ic_inuse) {
3068e50dcc9Sbrutus 			continue;
3078e50dcc9Sbrutus 		}
3088e50dcc9Sbrutus 
3098e50dcc9Sbrutus 		/*
3108e50dcc9Sbrutus 		 * Configure DMA channel
3118e50dcc9Sbrutus 		 *   Channel In Use
3128e50dcc9Sbrutus 		 *   Error Interrupt Enable
3138e50dcc9Sbrutus 		 *   Any Error Abort Enable
3148e50dcc9Sbrutus 		 *   Error Completion Enable
3158e50dcc9Sbrutus 		 */
3168e50dcc9Sbrutus 		ddi_put16(state->is_reg_handle,
3178e50dcc9Sbrutus 		    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x011C);
3188e50dcc9Sbrutus 
3198e50dcc9Sbrutus 		/* check channel error register, clear any errors */
3208e50dcc9Sbrutus 		estat = ddi_get32(state->is_reg_handle,
3218e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
3228e50dcc9Sbrutus 		if (estat != 0) {
3238e50dcc9Sbrutus #ifdef	DEBUG
3248e50dcc9Sbrutus 			cmn_err(CE_CONT, "cleared errors (0x%x) before channel"
3258e50dcc9Sbrutus 			    " (%d) enable\n", estat, channel->ic_chan_num);
3268e50dcc9Sbrutus #endif
3278e50dcc9Sbrutus 			ddi_put32(state->is_reg_handle,
3288e50dcc9Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR],
3298e50dcc9Sbrutus 			    estat);
3308e50dcc9Sbrutus 		}
3318e50dcc9Sbrutus 
3328e50dcc9Sbrutus 		/* Re-initialize the ring */
3338e50dcc9Sbrutus 		bzero(ring->cr_desc, channel->ic_desc_alloc_size);
3348e50dcc9Sbrutus 		/* write the physical address into the chain address register */
3358e50dcc9Sbrutus 		if (channel->ic_ver == IOAT_CBv1) {
3368e50dcc9Sbrutus 			ddi_put32(state->is_reg_handle,
3378e50dcc9Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO],
3388e50dcc9Sbrutus 			    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
3398e50dcc9Sbrutus 			ddi_put32(state->is_reg_handle,
3408e50dcc9Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI],
3418e50dcc9Sbrutus 			    (uint32_t)(ring->cr_phys_desc >> 32));
3428e50dcc9Sbrutus 		} else {
3438e50dcc9Sbrutus 			ASSERT(channel->ic_ver == IOAT_CBv2);
3448e50dcc9Sbrutus 			ddi_put32(state->is_reg_handle,
3458e50dcc9Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO],
3468e50dcc9Sbrutus 			    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
3478e50dcc9Sbrutus 			ddi_put32(state->is_reg_handle,
3488e50dcc9Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI],
3498e50dcc9Sbrutus 			    (uint32_t)(ring->cr_phys_desc >> 32));
3508e50dcc9Sbrutus 		}
3518e50dcc9Sbrutus 
3528e50dcc9Sbrutus 		/* re-initialize the completion buffer */
3538e50dcc9Sbrutus 		bzero((void *)channel->ic_cmpl, channel->ic_cmpl_alloc_size);
3548e50dcc9Sbrutus 		/* write the phys addr into the completion address register */
3558e50dcc9Sbrutus 		ddi_put32(state->is_reg_handle,
3568e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO],
3578e50dcc9Sbrutus 		    (uint32_t)(channel->ic_phys_cmpl & 0xffffffff));
3588e50dcc9Sbrutus 		ddi_put32(state->is_reg_handle,
3598e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI],
3608e50dcc9Sbrutus 		    (uint32_t)(channel->ic_phys_cmpl >> 32));
3618e50dcc9Sbrutus 
3628e50dcc9Sbrutus 		/* start-up the channel */
3638e50dcc9Sbrutus 		ioat_channel_start(channel);
3648e50dcc9Sbrutus 
3658e50dcc9Sbrutus 	}
3668e50dcc9Sbrutus 
3678e50dcc9Sbrutus 	return (DDI_SUCCESS);
3688e50dcc9Sbrutus }
3698e50dcc9Sbrutus 
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 
4048e50dcc9Sbrutus 
4058e50dcc9Sbrutus /*
4068e50dcc9Sbrutus  * ioat_channel_free()
4078e50dcc9Sbrutus  */
4088e50dcc9Sbrutus void
ioat_channel_free(void * channel_private)4098e50dcc9Sbrutus ioat_channel_free(void *channel_private)
4108e50dcc9Sbrutus {
4118e50dcc9Sbrutus 	struct ioat_channel_s *channel;
4128e50dcc9Sbrutus 	ioat_channel_t *chan;
4138e50dcc9Sbrutus 	ioat_state_t *state;
4148e50dcc9Sbrutus 	uint_t chan_num;
4158e50dcc9Sbrutus 
4168e50dcc9Sbrutus 
4178e50dcc9Sbrutus 	chan = (ioat_channel_t *)channel_private;
4188e50dcc9Sbrutus 	channel = *chan;
4198e50dcc9Sbrutus 
4208e50dcc9Sbrutus 	state = channel->ic_state;
4218e50dcc9Sbrutus 	chan_num = channel->ic_chan_num;
4228e50dcc9Sbrutus 
4238e50dcc9Sbrutus 	/* disable the interrupts */
4248e50dcc9Sbrutus 	ddi_put16(state->is_reg_handle,
4258e50dcc9Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x0);
4268e50dcc9Sbrutus 
4278e50dcc9Sbrutus 	ioat_channel_reset(channel);
4288e50dcc9Sbrutus 
4298e50dcc9Sbrutus 	/* cleanup command cache */
4308e50dcc9Sbrutus 	kmem_cache_destroy(channel->ic_cmd_cache);
4318e50dcc9Sbrutus 
4328e50dcc9Sbrutus 	/* clean-up/free-up the completion space and descriptors */
4338e50dcc9Sbrutus 	ioat_completion_free(channel);
4348e50dcc9Sbrutus 	ioat_ring_free(channel);
4358e50dcc9Sbrutus 
4368e50dcc9Sbrutus 	channel->ic_inuse = B_FALSE;
4378e50dcc9Sbrutus 
4388e50dcc9Sbrutus 	/* free the H/W DMA engine */
4398e50dcc9Sbrutus 	ioat_rs_free(state->is_channel_rs, chan_num);
4408e50dcc9Sbrutus 
4418e50dcc9Sbrutus 	*chan = NULL;
4428e50dcc9Sbrutus }
4438e50dcc9Sbrutus 
4448e50dcc9Sbrutus 
4458e50dcc9Sbrutus /*
4468e50dcc9Sbrutus  * ioat_channel_intr()
4478e50dcc9Sbrutus  */
4488e50dcc9Sbrutus void
ioat_channel_intr(ioat_channel_t channel)4498e50dcc9Sbrutus ioat_channel_intr(ioat_channel_t channel)
4508e50dcc9Sbrutus {
4518e50dcc9Sbrutus 	ioat_state_t *state;
4528e50dcc9Sbrutus 	uint16_t chanctrl;
4538e50dcc9Sbrutus 	uint32_t chanerr;
4548e50dcc9Sbrutus 	uint32_t status;
4558e50dcc9Sbrutus 
4568e50dcc9Sbrutus 
4578e50dcc9Sbrutus 	state = channel->ic_state;
4588e50dcc9Sbrutus 
4598e50dcc9Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
4608e50dcc9Sbrutus 		status = ddi_get32(state->is_reg_handle,
4618e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_STS_LO]);
4628e50dcc9Sbrutus 	} else {
4638e50dcc9Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
4648e50dcc9Sbrutus 		status = ddi_get32(state->is_reg_handle,
4658e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_STS_LO]);
4668e50dcc9Sbrutus 	}
4678e50dcc9Sbrutus 
4688e50dcc9Sbrutus 	/* if that status isn't ACTIVE or IDLE, the channel has failed */
4698e50dcc9Sbrutus 	if (status & IOAT_CHAN_STS_FAIL_MASK) {
4708e50dcc9Sbrutus 		chanerr = ddi_get32(state->is_reg_handle,
4718e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
4728e50dcc9Sbrutus 		cmn_err(CE_WARN, "channel(%d) fatal failure! "
4738e50dcc9Sbrutus 		    "chanstat_lo=0x%X; chanerr=0x%X\n",
4748e50dcc9Sbrutus 		    channel->ic_chan_num, status, chanerr);
4758e50dcc9Sbrutus 		channel->ic_channel_state = IOAT_CHANNEL_IN_FAILURE;
4768e50dcc9Sbrutus 		ioat_channel_reset(channel);
4778e50dcc9Sbrutus 
4788e50dcc9Sbrutus 		return;
4798e50dcc9Sbrutus 	}
4808e50dcc9Sbrutus 
4818e50dcc9Sbrutus 	/*
4828e50dcc9Sbrutus 	 * clear interrupt disable bit if set (it's a RW1C). Read it back to
4838e50dcc9Sbrutus 	 * ensure the write completes.
4848e50dcc9Sbrutus 	 */
4858e50dcc9Sbrutus 	chanctrl = ddi_get16(state->is_reg_handle,
4868e50dcc9Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL]);
4878e50dcc9Sbrutus 	ddi_put16(state->is_reg_handle,
4888e50dcc9Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], chanctrl);
4898e50dcc9Sbrutus 	(void) ddi_get16(state->is_reg_handle,
4908e50dcc9Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL]);
4918e50dcc9Sbrutus 
4928e50dcc9Sbrutus 	/* tell dcopy we have seen a completion on this channel */
4938e50dcc9Sbrutus 	dcopy_device_channel_notify(channel->ic_dcopy_handle, DCOPY_COMPLETION);
4948e50dcc9Sbrutus }
4958e50dcc9Sbrutus 
4968e50dcc9Sbrutus 
4978e50dcc9Sbrutus /*
4988e50dcc9Sbrutus  * ioat_channel_start()
4998e50dcc9Sbrutus  */
5008e50dcc9Sbrutus void
ioat_channel_start(ioat_channel_t channel)5018e50dcc9Sbrutus ioat_channel_start(ioat_channel_t channel)
5028e50dcc9Sbrutus {
5038e50dcc9Sbrutus 	ioat_chan_dma_desc_t desc;
5048e50dcc9Sbrutus 
5058e50dcc9Sbrutus 	/* set the first descriptor up as a NULL descriptor */
5068e50dcc9Sbrutus 	bzero(&desc, sizeof (desc));
5078e50dcc9Sbrutus 	desc.dd_size = 0;
5088e50dcc9Sbrutus 	desc.dd_ctrl = IOAT_DESC_CTRL_OP_DMA | IOAT_DESC_DMACTRL_NULL |
5098e50dcc9Sbrutus 	    IOAT_DESC_CTRL_CMPL;
5108e50dcc9Sbrutus 	desc.dd_next_desc = 0x0;
5118e50dcc9Sbrutus 
5128e50dcc9Sbrutus 	/* setup the very first descriptor */
5138e50dcc9Sbrutus 	ioat_ring_seed(channel, &desc);
5148e50dcc9Sbrutus }
5158e50dcc9Sbrutus 
5168e50dcc9Sbrutus 
5178e50dcc9Sbrutus /*
5188e50dcc9Sbrutus  * ioat_channel_reset()
5198e50dcc9Sbrutus  */
5208e50dcc9Sbrutus void
ioat_channel_reset(ioat_channel_t channel)5218e50dcc9Sbrutus ioat_channel_reset(ioat_channel_t channel)
5228e50dcc9Sbrutus {
5238e50dcc9Sbrutus 	ioat_state_t *state;
5248e50dcc9Sbrutus 
5258e50dcc9Sbrutus 	state = channel->ic_state;
5268e50dcc9Sbrutus 
5278e50dcc9Sbrutus 	/* hit the reset bit */
5288e50dcc9Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
5298e50dcc9Sbrutus 		ddi_put8(state->is_reg_handle,
5308e50dcc9Sbrutus 		    &channel->ic_regs[IOAT_V1_CHAN_CMD], 0x20);
5318e50dcc9Sbrutus 	} else {
5328e50dcc9Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
5338e50dcc9Sbrutus 		ddi_put8(state->is_reg_handle,
5348e50dcc9Sbrutus 		    &channel->ic_regs[IOAT_V2_CHAN_CMD], 0x20);
5358e50dcc9Sbrutus 	}
5368e50dcc9Sbrutus }
5378e50dcc9Sbrutus 
5388e50dcc9Sbrutus 
5398e50dcc9Sbrutus /*
5408e50dcc9Sbrutus  * ioat_completion_alloc()
5418e50dcc9Sbrutus  */
5428e50dcc9Sbrutus int
ioat_completion_alloc(ioat_channel_t channel)5438e50dcc9Sbrutus ioat_completion_alloc(ioat_channel_t channel)
5448e50dcc9Sbrutus {
5458e50dcc9Sbrutus 	ioat_state_t *state;
5468e50dcc9Sbrutus 	size_t real_length;
5478e50dcc9Sbrutus 	uint_t cookie_cnt;
5488e50dcc9Sbrutus 	int e;
5498e50dcc9Sbrutus 
5508e50dcc9Sbrutus 
5518e50dcc9Sbrutus 	state = channel->ic_state;
5528e50dcc9Sbrutus 
5538e50dcc9Sbrutus 	/*
5548e50dcc9Sbrutus 	 * allocate memory for the completion status, zero it out, and get
5558e50dcc9Sbrutus 	 * the paddr. We'll allocate a physically contiguous cache line.
5568e50dcc9Sbrutus 	 */
5578e50dcc9Sbrutus 	e = ddi_dma_alloc_handle(state->is_dip, &ioat_cmpl_dma_attr,
5588e50dcc9Sbrutus 	    DDI_DMA_SLEEP, NULL, &channel->ic_cmpl_dma_handle);
5598e50dcc9Sbrutus 	if (e != DDI_SUCCESS) {
5608e50dcc9Sbrutus 		goto cmplallocfail_alloc_handle;
5618e50dcc9Sbrutus 	}
5628e50dcc9Sbrutus 	channel->ic_cmpl_alloc_size = 64;
5638e50dcc9Sbrutus 	e = ddi_dma_mem_alloc(channel->ic_cmpl_dma_handle,
5648e50dcc9Sbrutus 	    channel->ic_cmpl_alloc_size, &ioat_acc_attr,
5658e50dcc9Sbrutus 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
5668e50dcc9Sbrutus 	    (caddr_t *)&channel->ic_cmpl, &real_length,
5678e50dcc9Sbrutus 	    &channel->ic_cmpl_handle);
5688e50dcc9Sbrutus 	if (e != DDI_SUCCESS) {
5698e50dcc9Sbrutus 		goto cmplallocfail_mem_alloc;
5708e50dcc9Sbrutus 	}
5718e50dcc9Sbrutus 	bzero((void *)channel->ic_cmpl, channel->ic_cmpl_alloc_size);
5728e50dcc9Sbrutus 	e = ddi_dma_addr_bind_handle(channel->ic_cmpl_dma_handle, NULL,
5738e50dcc9Sbrutus 	    (caddr_t)channel->ic_cmpl, channel->ic_cmpl_alloc_size,
5748e50dcc9Sbrutus 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
5758e50dcc9Sbrutus 	    &channel->ic_cmpl_cookie, &cookie_cnt);
5768e50dcc9Sbrutus 	if (e != DDI_SUCCESS) {
5778e50dcc9Sbrutus 		goto cmplallocfail_addr_bind;
5788e50dcc9Sbrutus 	}
5798e50dcc9Sbrutus 	ASSERT(cookie_cnt == 1);
5808e50dcc9Sbrutus 	ASSERT(channel->ic_cmpl_cookie.dmac_size ==
5818e50dcc9Sbrutus 	    channel->ic_cmpl_alloc_size);
5828e50dcc9Sbrutus 	channel->ic_phys_cmpl = channel->ic_cmpl_cookie.dmac_laddress;
5838e50dcc9Sbrutus 
5848e50dcc9Sbrutus 	/* write the physical address into the completion address register */
5858e50dcc9Sbrutus 	ddi_put32(state->is_reg_handle,
5868e50dcc9Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO],
5878e50dcc9Sbrutus 	    (uint32_t)(channel->ic_phys_cmpl & 0xffffffff));
5888e50dcc9Sbrutus 	ddi_put32(state->is_reg_handle,
5898e50dcc9Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI],
5908e50dcc9Sbrutus 	    (uint32_t)(channel->ic_phys_cmpl >> 32));
5918e50dcc9Sbrutus 
5928e50dcc9Sbrutus 	return (DDI_SUCCESS);
5938e50dcc9Sbrutus 
5948e50dcc9Sbrutus cmplallocfail_addr_bind:
5958e50dcc9Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
5968e50dcc9Sbrutus cmplallocfail_mem_alloc:
5978e50dcc9Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
5988e50dcc9Sbrutus cmplallocfail_alloc_handle:
5998e50dcc9Sbrutus 	return (DDI_FAILURE);
6008e50dcc9Sbrutus }
6018e50dcc9Sbrutus 
6028e50dcc9Sbrutus 
6038e50dcc9Sbrutus /*
6048e50dcc9Sbrutus  * ioat_completion_free()
6058e50dcc9Sbrutus  */
6068e50dcc9Sbrutus void
ioat_completion_free(ioat_channel_t channel)6078e50dcc9Sbrutus ioat_completion_free(ioat_channel_t channel)
6088e50dcc9Sbrutus {
6098e50dcc9Sbrutus 	ioat_state_t *state;
6108e50dcc9Sbrutus 
6118e50dcc9Sbrutus 	state = channel->ic_state;
6128e50dcc9Sbrutus 
6138e50dcc9Sbrutus 	/* reset the completion address register */
6148e50dcc9Sbrutus 	ddi_put32(state->is_reg_handle,
6158e50dcc9Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO], 0x0);
6168e50dcc9Sbrutus 	ddi_put32(state->is_reg_handle,
6178e50dcc9Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI], 0x0);
6188e50dcc9Sbrutus 
6198e50dcc9Sbrutus 	/* unbind, then free up the memory, dma handle */
6208e50dcc9Sbrutus 	(void) ddi_dma_unbind_handle(channel->ic_cmpl_dma_handle);
6218e50dcc9Sbrutus 	ddi_dma_mem_free(&channel->ic_cmpl_handle);
6228e50dcc9Sbrutus 	ddi_dma_free_handle(&channel->ic_cmpl_dma_handle);
6238e50dcc9Sbrutus }
6248e50dcc9Sbrutus 
6258e50dcc9Sbrutus /*
6268e50dcc9Sbrutus  * ioat_ring_alloc()
6278e50dcc9Sbrutus  */
6288e50dcc9Sbrutus int
ioat_ring_alloc(ioat_channel_t channel,uint_t desc_cnt)6298e50dcc9Sbrutus ioat_ring_alloc(ioat_channel_t channel, uint_t desc_cnt)
6308e50dcc9Sbrutus {
6318e50dcc9Sbrutus 	ioat_channel_ring_t *ring;
6328e50dcc9Sbrutus 	ioat_state_t *state;
6338e50dcc9Sbrutus 	size_t real_length;
6348e50dcc9Sbrutus 	uint_t cookie_cnt;
6358e50dcc9Sbrutus 	int e;
6368e50dcc9Sbrutus 
6378e50dcc9Sbrutus 
6388e50dcc9Sbrutus 	state = channel->ic_state;
6398e50dcc9Sbrutus 
6408e50dcc9Sbrutus 	ring = kmem_zalloc(sizeof (ioat_channel_ring_t), KM_SLEEP);
6418e50dcc9Sbrutus 	channel->ic_ring = ring;
6428e50dcc9Sbrutus 	ring->cr_chan = channel;
6438e50dcc9Sbrutus 	ring->cr_post_cnt = 0;
6448e50dcc9Sbrutus 
6458e50dcc9Sbrutus 	mutex_init(&ring->cr_cmpl_mutex, NULL, MUTEX_DRIVER,
6468e50dcc9Sbrutus 	    channel->ic_state->is_iblock_cookie);
6478e50dcc9Sbrutus 	mutex_init(&ring->cr_desc_mutex, NULL, MUTEX_DRIVER,
6488e50dcc9Sbrutus 	    channel->ic_state->is_iblock_cookie);
6498e50dcc9Sbrutus 
6508e50dcc9Sbrutus 	/*
6518e50dcc9Sbrutus 	 * allocate memory for the ring, zero it out, and get the paddr.
6528e50dcc9Sbrutus 	 * We'll allocate a physically contiguous chunck of memory  which
6538e50dcc9Sbrutus 	 * simplifies the completion logic.
6548e50dcc9Sbrutus 	 */
6558e50dcc9Sbrutus 	e = ddi_dma_alloc_handle(state->is_dip, &ioat_desc_dma_attr,
6568e50dcc9Sbrutus 	    DDI_DMA_SLEEP, NULL, &channel->ic_desc_dma_handle);
6578e50dcc9Sbrutus 	if (e != DDI_SUCCESS) {
6588e50dcc9Sbrutus 		goto ringallocfail_alloc_handle;
6598e50dcc9Sbrutus 	}
6608e50dcc9Sbrutus 	/*
6618e50dcc9Sbrutus 	 * allocate one extra descriptor so we can simplify the empty/full
6628e50dcc9Sbrutus 	 * logic. Then round that number up to a whole multiple of 4.
6638e50dcc9Sbrutus 	 */
6648e50dcc9Sbrutus 	channel->ic_chan_desc_cnt = ((desc_cnt + 1) + 3) & ~0x3;
6658e50dcc9Sbrutus 	ring->cr_desc_last = channel->ic_chan_desc_cnt - 1;
6668e50dcc9Sbrutus 	channel->ic_desc_alloc_size = channel->ic_chan_desc_cnt *
6678e50dcc9Sbrutus 	    sizeof (ioat_chan_desc_t);
6688e50dcc9Sbrutus 	e = ddi_dma_mem_alloc(channel->ic_desc_dma_handle,
6698e50dcc9Sbrutus 	    channel->ic_desc_alloc_size, &ioat_acc_attr,
6708e50dcc9Sbrutus 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
6718e50dcc9Sbrutus 	    (caddr_t *)&ring->cr_desc, &real_length, &channel->ic_desc_handle);
6728e50dcc9Sbrutus 	if (e != DDI_SUCCESS) {
6738e50dcc9Sbrutus 		goto ringallocfail_mem_alloc;
6748e50dcc9Sbrutus 	}
6758e50dcc9Sbrutus 	bzero(ring->cr_desc, channel->ic_desc_alloc_size);
6768e50dcc9Sbrutus 	e = ddi_dma_addr_bind_handle(channel->ic_desc_dma_handle, NULL,
6778e50dcc9Sbrutus 	    (caddr_t)ring->cr_desc, channel->ic_desc_alloc_size,
6788e50dcc9Sbrutus 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
6798e50dcc9Sbrutus 	    &channel->ic_desc_cookies, &cookie_cnt);
6808e50dcc9Sbrutus 	if (e != DDI_SUCCESS) {
6818e50dcc9Sbrutus 		goto ringallocfail_addr_bind;
6828e50dcc9Sbrutus 	}
6838e50dcc9Sbrutus 	ASSERT(cookie_cnt == 1);
6848e50dcc9Sbrutus 	ASSERT(channel->ic_desc_cookies.dmac_size ==
6858e50dcc9Sbrutus 	    channel->ic_desc_alloc_size);
6868e50dcc9Sbrutus 	ring->cr_phys_desc = channel->ic_desc_cookies.dmac_laddress;
6878e50dcc9Sbrutus 
6888e50dcc9Sbrutus 	/* write the physical address into the chain address register */
6898e50dcc9Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
6908e50dcc9Sbrutus 		ddi_put32(state->is_reg_handle,
6918e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO],
6928e50dcc9Sbrutus 		    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
6938e50dcc9Sbrutus 		ddi_put32(state->is_reg_handle,
6948e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI],
6958e50dcc9Sbrutus 		    (uint32_t)(ring->cr_phys_desc >> 32));
6968e50dcc9Sbrutus 	} else {
6978e50dcc9Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
6988e50dcc9Sbrutus 		ddi_put32(state->is_reg_handle,
6998e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO],
7008e50dcc9Sbrutus 		    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
7018e50dcc9Sbrutus 		ddi_put32(state->is_reg_handle,
7028e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI],
7038e50dcc9Sbrutus 		    (uint32_t)(ring->cr_phys_desc >> 32));
7048e50dcc9Sbrutus 	}
7058e50dcc9Sbrutus 
7068e50dcc9Sbrutus 	return (DCOPY_SUCCESS);
7078e50dcc9Sbrutus 
7088e50dcc9Sbrutus ringallocfail_addr_bind:
7098e50dcc9Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
7108e50dcc9Sbrutus ringallocfail_mem_alloc:
7118e50dcc9Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
7128e50dcc9Sbrutus ringallocfail_alloc_handle:
7138e50dcc9Sbrutus 	mutex_destroy(&ring->cr_desc_mutex);
7148e50dcc9Sbrutus 	mutex_destroy(&ring->cr_cmpl_mutex);
7158e50dcc9Sbrutus 	kmem_free(channel->ic_ring, sizeof (ioat_channel_ring_t));
7168e50dcc9Sbrutus 
7178e50dcc9Sbrutus 	return (DCOPY_FAILURE);
7188e50dcc9Sbrutus }
7198e50dcc9Sbrutus 
7208e50dcc9Sbrutus 
7218e50dcc9Sbrutus /*
7228e50dcc9Sbrutus  * ioat_ring_free()
7238e50dcc9Sbrutus  */
7248e50dcc9Sbrutus void
ioat_ring_free(ioat_channel_t channel)7258e50dcc9Sbrutus ioat_ring_free(ioat_channel_t channel)
7268e50dcc9Sbrutus {
7278e50dcc9Sbrutus 	ioat_state_t *state;
7288e50dcc9Sbrutus 
7298e50dcc9Sbrutus 
7308e50dcc9Sbrutus 	state = channel->ic_state;
7318e50dcc9Sbrutus 
7328e50dcc9Sbrutus 	/* reset the chain address register */
7338e50dcc9Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
7348e50dcc9Sbrutus 		ddi_put32(state->is_reg_handle,
7358e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO], 0x0);
7368e50dcc9Sbrutus 		ddi_put32(state->is_reg_handle,
7378e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI], 0x0);
7388e50dcc9Sbrutus 	} else {
7398e50dcc9Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
7408e50dcc9Sbrutus 		ddi_put32(state->is_reg_handle,
7418e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO], 0x0);
7428e50dcc9Sbrutus 		ddi_put32(state->is_reg_handle,
7438e50dcc9Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI], 0x0);
7448e50dcc9Sbrutus 	}
7458e50dcc9Sbrutus 
7468e50dcc9Sbrutus 	/* unbind, then free up the memory, dma handle */
7478e50dcc9Sbrutus 	(void) ddi_dma_unbind_handle(channel->ic_desc_dma_handle);
7488e50dcc9Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
7498e50dcc9Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
7508e50dcc9Sbrutus 
7518e50dcc9Sbrutus 	mutex_destroy(&channel->ic_ring->cr_desc_mutex);
7528e50dcc9Sbrutus 	mutex_destroy(&channel->ic_ring->cr_cmpl_mutex);
7538e50dcc9Sbrutus 	kmem_free(channel->ic_ring, sizeof (ioat_channel_ring_t));
7548e50dcc9Sbrutus 
7558e50dcc9Sbrutus }
7568e50dcc9Sbrutus 
7578e50dcc9Sbrutus 
7588e50dcc9Sbrutus /*
7598e50dcc9Sbrutus  * ioat_ring_seed()
7608e50dcc9Sbrutus  *    write the first descriptor in the ring.
7618e50dcc9Sbrutus  */
7628e50dcc9Sbrutus void
ioat_ring_seed(ioat_channel_t channel,ioat_chan_dma_desc_t * in_desc)7638e50dcc9Sbrutus ioat_ring_seed(ioat_channel_t channel, ioat_chan_dma_desc_t *in_desc)
7648e50dcc9Sbrutus {
7658e50dcc9Sbrutus 	ioat_channel_ring_t *ring;
7668e50dcc9Sbrutus 	ioat_chan_dma_desc_t *desc;
7678e50dcc9Sbrutus 	ioat_chan_dma_desc_t *prev;
7688e50dcc9Sbrutus 	ioat_state_t *state;
7698e50dcc9Sbrutus 
7708e50dcc9Sbrutus 
7718e50dcc9Sbrutus 	state = channel->ic_state;
7728e50dcc9Sbrutus 	ring = channel->ic_ring;
7738e50dcc9Sbrutus 
7748e50dcc9Sbrutus 	/* init the completion state */
7758e50dcc9Sbrutus 	ring->cr_cmpl_gen = 0x0;
7768e50dcc9Sbrutus 	ring->cr_cmpl_last = 0x0;
7778e50dcc9Sbrutus 
7788e50dcc9Sbrutus 	/* write in the descriptor and init the descriptor state */
7798e50dcc9Sbrutus 	ring->cr_post_cnt++;
7808e50dcc9Sbrutus 	channel->ic_ring->cr_desc[0] = *(ioat_chan_desc_t *)in_desc;
7818e50dcc9Sbrutus 	ring->cr_desc_gen = 0;
7828e50dcc9Sbrutus 	ring->cr_desc_prev = 0;
7838e50dcc9Sbrutus 	ring->cr_desc_next = 1;
7848e50dcc9Sbrutus 
7858e50dcc9Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
7868e50dcc9Sbrutus 		/* hit the start bit */
7878e50dcc9Sbrutus 		ddi_put8(state->is_reg_handle,
7888e50dcc9Sbrutus 		    &channel->ic_regs[IOAT_V1_CHAN_CMD], 0x1);
7898e50dcc9Sbrutus 	} else {
7908e50dcc9Sbrutus 		/*
7918e50dcc9Sbrutus 		 * if this is CBv2, link the descriptor to an empty
7928e50dcc9Sbrutus 		 * descriptor
7938e50dcc9Sbrutus 		 */
7948e50dcc9Sbrutus 		ASSERT(ring->cr_chan->ic_ver == IOAT_CBv2);
7958e50dcc9Sbrutus 		desc = (ioat_chan_dma_desc_t *)
7968e50dcc9Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
7978e50dcc9Sbrutus 		prev = (ioat_chan_dma_desc_t *)
7988e50dcc9Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
7998e50dcc9Sbrutus 
8008e50dcc9Sbrutus 		desc->dd_ctrl = 0;
8018e50dcc9Sbrutus 		desc->dd_next_desc = 0x0;
8028e50dcc9Sbrutus 
8038e50dcc9Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
8048e50dcc9Sbrutus 		    (ring->cr_desc_next << 6);
8058e50dcc9Sbrutus 
8068e50dcc9Sbrutus 		ddi_put16(state->is_reg_handle,
8078e50dcc9Sbrutus 		    (uint16_t *)&channel->ic_regs[IOAT_V2_CHAN_CNT],
8088e50dcc9Sbrutus 		    (uint16_t)1);
8098e50dcc9Sbrutus 	}
8108e50dcc9Sbrutus 
8118e50dcc9Sbrutus }
8128e50dcc9Sbrutus 
813eca2601cSRandy Fishel /*
814eca2601cSRandy Fishel  * ioat_ring_loop()
815eca2601cSRandy Fishel  * Make the ring loop for CB v1
816eca2601cSRandy Fishel  * This function assume we are in the ring->cr_desc_mutex mutex context
817eca2601cSRandy Fishel  */
818eca2601cSRandy Fishel int
ioat_ring_loop(ioat_channel_ring_t * ring,dcopy_cmd_t cmd)819eca2601cSRandy Fishel ioat_ring_loop(ioat_channel_ring_t *ring, dcopy_cmd_t cmd)
820eca2601cSRandy Fishel {
821eca2601cSRandy Fishel 	uint64_t count;
822eca2601cSRandy Fishel 	ioat_channel_t channel;
823eca2601cSRandy Fishel 	ioat_chan_dma_desc_t *curr;
824eca2601cSRandy Fishel 	ioat_cmd_private_t *prevpriv;
825eca2601cSRandy Fishel 	ioat_cmd_private_t *currpriv;
826eca2601cSRandy Fishel 
827*584b574aSToomas Soome 	currpriv = NULL;
828eca2601cSRandy Fishel 	channel = ring->cr_chan;
829eca2601cSRandy Fishel 	ASSERT(channel->ic_ver == IOAT_CBv1);
830eca2601cSRandy Fishel 
831eca2601cSRandy Fishel 	/*
832eca2601cSRandy Fishel 	 * For each cmd in the command queue, check whether they are continuous
833eca2601cSRandy Fishel 	 * in descriptor ring. Return error if not continuous.
834eca2601cSRandy Fishel 	 */
835eca2601cSRandy Fishel 	for (count = 0, prevpriv = NULL;
836eca2601cSRandy Fishel 	    cmd != NULL && count <= channel->ic_chan_desc_cnt;
837eca2601cSRandy Fishel 	    prevpriv = currpriv) {
838eca2601cSRandy Fishel 		currpriv = cmd->dp_private->pr_device_cmd_private;
839eca2601cSRandy Fishel 		if (prevpriv != NULL &&
840eca2601cSRandy Fishel 		    currpriv->ip_index + 1 != prevpriv->ip_start &&
841eca2601cSRandy Fishel 		    currpriv->ip_index + 1 != prevpriv->ip_start +
842eca2601cSRandy Fishel 		    channel->ic_chan_desc_cnt) {
843eca2601cSRandy Fishel 			/* Non-continuous, other commands get interleaved */
844eca2601cSRandy Fishel 			return (DCOPY_FAILURE);
845eca2601cSRandy Fishel 		}
846eca2601cSRandy Fishel 		if (currpriv->ip_index < currpriv->ip_start) {
847eca2601cSRandy Fishel 			count += channel->ic_chan_desc_cnt
848eca2601cSRandy Fishel 			    + currpriv->ip_index - currpriv->ip_start + 1;
849eca2601cSRandy Fishel 		} else {
850eca2601cSRandy Fishel 			count += currpriv->ip_index - currpriv->ip_start + 1;
851eca2601cSRandy Fishel 		}
852eca2601cSRandy Fishel 		cmd = currpriv->ip_next;
853eca2601cSRandy Fishel 	}
854eca2601cSRandy Fishel 	/*
855eca2601cSRandy Fishel 	 * Check for too many descriptors which would cause wrap around in
856eca2601cSRandy Fishel 	 * descriptor ring. And make sure there is space for cancel operation.
857eca2601cSRandy Fishel 	 */
858eca2601cSRandy Fishel 	if (count >= channel->ic_chan_desc_cnt) {
859eca2601cSRandy Fishel 		return (DCOPY_FAILURE);
860eca2601cSRandy Fishel 	}
861eca2601cSRandy Fishel 
862eca2601cSRandy Fishel 	/* Point next descriptor to header of chain. */
863eca2601cSRandy Fishel 	curr = (ioat_chan_dma_desc_t *)&ring->cr_desc[ring->cr_desc_prev];
864eca2601cSRandy Fishel 	curr->dd_next_desc = ring->cr_phys_desc + (currpriv->ip_start << 6);
865eca2601cSRandy Fishel 
866eca2601cSRandy Fishel 	/* sync the last desc */
867eca2601cSRandy Fishel 	(void) ddi_dma_sync(channel->ic_desc_dma_handle,
868eca2601cSRandy Fishel 	    ring->cr_desc_prev << 6, 64, DDI_DMA_SYNC_FORDEV);
869eca2601cSRandy Fishel 
870eca2601cSRandy Fishel 	return (DCOPY_SUCCESS);
871eca2601cSRandy Fishel }
872eca2601cSRandy Fishel 
8738e50dcc9Sbrutus 
8748e50dcc9Sbrutus /*
8758e50dcc9Sbrutus  * ioat_cmd_alloc()
8768e50dcc9Sbrutus  */
8778e50dcc9Sbrutus int
ioat_cmd_alloc(void * private,int flags,dcopy_cmd_t * cmd)8788e50dcc9Sbrutus ioat_cmd_alloc(void *private, int flags, dcopy_cmd_t *cmd)
8798e50dcc9Sbrutus {
8808e50dcc9Sbrutus 	ioat_cmd_private_t *priv;
8818e50dcc9Sbrutus 	ioat_channel_t channel;
8828e50dcc9Sbrutus 	dcopy_cmd_t oldcmd;
8838e50dcc9Sbrutus 	int kmflag;
8848e50dcc9Sbrutus 
8858e50dcc9Sbrutus 
8868e50dcc9Sbrutus 	channel = (ioat_channel_t)private;
8878e50dcc9Sbrutus 
8888e50dcc9Sbrutus 	if (flags & DCOPY_NOSLEEP) {
8898e50dcc9Sbrutus 		kmflag = KM_NOSLEEP;
8908e50dcc9Sbrutus 	} else {
8918e50dcc9Sbrutus 		kmflag = KM_SLEEP;
8928e50dcc9Sbrutus 	}
8938e50dcc9Sbrutus 
8948e50dcc9Sbrutus 	/* save the command passed incase DCOPY_ALLOC_LINK is set */
8958e50dcc9Sbrutus 	oldcmd = *cmd;
8968e50dcc9Sbrutus 
8978e50dcc9Sbrutus 	*cmd = kmem_cache_alloc(channel->ic_cmd_cache, kmflag);
8988e50dcc9Sbrutus 	if (*cmd == NULL) {
8998e50dcc9Sbrutus 		return (DCOPY_NORESOURCES);
9008e50dcc9Sbrutus 	}
9018e50dcc9Sbrutus 
9028e50dcc9Sbrutus 	/* setup the dcopy and ioat private state pointers */
9038e50dcc9Sbrutus 	(*cmd)->dp_version = DCOPY_CMD_V0;
9048e50dcc9Sbrutus 	(*cmd)->dp_cmd = 0;
9058e50dcc9Sbrutus 	(*cmd)->dp_private = (struct dcopy_cmd_priv_s *)
9068e50dcc9Sbrutus 	    ((uintptr_t)(*cmd) + sizeof (struct dcopy_cmd_s));
9078e50dcc9Sbrutus 	(*cmd)->dp_private->pr_device_cmd_private =
9088e50dcc9Sbrutus 	    (struct ioat_cmd_private_s *)((uintptr_t)(*cmd)->dp_private +
9098e50dcc9Sbrutus 	    sizeof (struct dcopy_cmd_priv_s));
9108e50dcc9Sbrutus 
9118e50dcc9Sbrutus 	/*
9128e50dcc9Sbrutus 	 * if DCOPY_ALLOC_LINK is set, link the old command to the new one
9138e50dcc9Sbrutus 	 * just allocated.
9148e50dcc9Sbrutus 	 */
9158e50dcc9Sbrutus 	priv = (*cmd)->dp_private->pr_device_cmd_private;
9168e50dcc9Sbrutus 	if (flags & DCOPY_ALLOC_LINK) {
9178e50dcc9Sbrutus 		priv->ip_next = oldcmd;
9188e50dcc9Sbrutus 	} else {
9198e50dcc9Sbrutus 		priv->ip_next = NULL;
9208e50dcc9Sbrutus 	}
9218e50dcc9Sbrutus 
9228e50dcc9Sbrutus 	return (DCOPY_SUCCESS);
9238e50dcc9Sbrutus }
9248e50dcc9Sbrutus 
9258e50dcc9Sbrutus 
9268e50dcc9Sbrutus /*
9278e50dcc9Sbrutus  * ioat_cmd_free()
9288e50dcc9Sbrutus  */
9298e50dcc9Sbrutus void
ioat_cmd_free(void * private,dcopy_cmd_t * cmdp)9308e50dcc9Sbrutus ioat_cmd_free(void *private, dcopy_cmd_t *cmdp)
9318e50dcc9Sbrutus {
9328e50dcc9Sbrutus 	ioat_cmd_private_t *priv;
9338e50dcc9Sbrutus 	ioat_channel_t channel;
9348e50dcc9Sbrutus 	dcopy_cmd_t next;
9358e50dcc9Sbrutus 	dcopy_cmd_t cmd;
9368e50dcc9Sbrutus 
9378e50dcc9Sbrutus 
9388e50dcc9Sbrutus 	channel = (ioat_channel_t)private;
9398e50dcc9Sbrutus 	cmd = *(cmdp);
9408e50dcc9Sbrutus 
9418e50dcc9Sbrutus 	/*
9428e50dcc9Sbrutus 	 * free all the commands in the chain (see DCOPY_ALLOC_LINK in
9438e50dcc9Sbrutus 	 * ioat_cmd_alloc() for more info).
9448e50dcc9Sbrutus 	 */
9458e50dcc9Sbrutus 	while (cmd != NULL) {
9468e50dcc9Sbrutus 		priv = cmd->dp_private->pr_device_cmd_private;
9478e50dcc9Sbrutus 		next = priv->ip_next;
9488e50dcc9Sbrutus 		kmem_cache_free(channel->ic_cmd_cache, cmd);
9498e50dcc9Sbrutus 		cmd = next;
9508e50dcc9Sbrutus 	}
9518e50dcc9Sbrutus 	*cmdp = NULL;
9528e50dcc9Sbrutus }
9538e50dcc9Sbrutus 
9548e50dcc9Sbrutus 
9558e50dcc9Sbrutus /*
9568e50dcc9Sbrutus  * ioat_cmd_post()
9578e50dcc9Sbrutus  */
9588e50dcc9Sbrutus int
ioat_cmd_post(void * private,dcopy_cmd_t cmd)9598e50dcc9Sbrutus ioat_cmd_post(void *private, dcopy_cmd_t cmd)
9608e50dcc9Sbrutus {
9618e50dcc9Sbrutus 	ioat_channel_ring_t *ring;
9628e50dcc9Sbrutus 	ioat_cmd_private_t *priv;
9638e50dcc9Sbrutus 	ioat_channel_t channel;
9648e50dcc9Sbrutus 	ioat_state_t *state;
9658e50dcc9Sbrutus 	uint64_t dest_paddr;
9668e50dcc9Sbrutus 	uint64_t src_paddr;
9678e50dcc9Sbrutus 	uint64_t dest_addr;
9688e50dcc9Sbrutus 	uint32_t dest_size;
9698e50dcc9Sbrutus 	uint64_t src_addr;
9708e50dcc9Sbrutus 	uint32_t src_size;
9718e50dcc9Sbrutus 	size_t xfer_size;
9728e50dcc9Sbrutus 	uint32_t ctrl;
9738e50dcc9Sbrutus 	size_t size;
9748e50dcc9Sbrutus 	int e;
9758e50dcc9Sbrutus 
9768e50dcc9Sbrutus 
9778e50dcc9Sbrutus 	channel = (ioat_channel_t)private;
9788e50dcc9Sbrutus 	priv = cmd->dp_private->pr_device_cmd_private;
9798e50dcc9Sbrutus 
9808e50dcc9Sbrutus 	state = channel->ic_state;
9818e50dcc9Sbrutus 	ring = channel->ic_ring;
9828e50dcc9Sbrutus 
983eca2601cSRandy Fishel 	/*
984eca2601cSRandy Fishel 	 * Special support for DCOPY_CMD_LOOP option, only supported on CBv1.
985eca2601cSRandy Fishel 	 * DCOPY_CMD_QUEUE should also be set if DCOPY_CMD_LOOP is set.
986eca2601cSRandy Fishel 	 */
987eca2601cSRandy Fishel 	if ((cmd->dp_flags & DCOPY_CMD_LOOP) &&
988eca2601cSRandy Fishel 	    (channel->ic_ver != IOAT_CBv1 ||
989eca2601cSRandy Fishel 	    (cmd->dp_flags & DCOPY_CMD_QUEUE))) {
990eca2601cSRandy Fishel 		return (DCOPY_FAILURE);
991eca2601cSRandy Fishel 	}
992eca2601cSRandy Fishel 
993eca2601cSRandy Fishel 	if ((cmd->dp_flags & DCOPY_CMD_NOWAIT) == 0) {
994eca2601cSRandy Fishel 		mutex_enter(&ring->cr_desc_mutex);
995eca2601cSRandy Fishel 
996eca2601cSRandy Fishel 	/*
997eca2601cSRandy Fishel 	 * Try to acquire mutex if NOWAIT flag is set.
998eca2601cSRandy Fishel 	 * Return failure if failed to acquire mutex.
999eca2601cSRandy Fishel 	 */
1000eca2601cSRandy Fishel 	} else if (mutex_tryenter(&ring->cr_desc_mutex) == 0) {
1001eca2601cSRandy Fishel 		return (DCOPY_FAILURE);
1002eca2601cSRandy Fishel 	}
10038e50dcc9Sbrutus 
10048e50dcc9Sbrutus 	/* if the channel has had a fatal failure, return failure */
10058e50dcc9Sbrutus 	if (channel->ic_channel_state == IOAT_CHANNEL_IN_FAILURE) {
10064f0f65c2SMark Johnson 		mutex_exit(&ring->cr_desc_mutex);
10078e50dcc9Sbrutus 		return (DCOPY_FAILURE);
10088e50dcc9Sbrutus 	}
10098e50dcc9Sbrutus 
10108e50dcc9Sbrutus 	/* make sure we have space for the descriptors */
10118e50dcc9Sbrutus 	e = ioat_ring_reserve(channel, ring, cmd);
10128e50dcc9Sbrutus 	if (e != DCOPY_SUCCESS) {
10134f0f65c2SMark Johnson 		mutex_exit(&ring->cr_desc_mutex);
10148e50dcc9Sbrutus 		return (DCOPY_NORESOURCES);
10158e50dcc9Sbrutus 	}
10168e50dcc9Sbrutus 
10178e50dcc9Sbrutus 	/* if we support DCA, and the DCA flag is set, post a DCA desc */
10188e50dcc9Sbrutus 	if ((channel->ic_ver == IOAT_CBv2) &&
10198e50dcc9Sbrutus 	    (cmd->dp_flags & DCOPY_CMD_DCA)) {
10208e50dcc9Sbrutus 		ioat_cmd_post_dca(ring, cmd->dp_dca_id);
10218e50dcc9Sbrutus 	}
10228e50dcc9Sbrutus 
10238e50dcc9Sbrutus 	/*
10248e50dcc9Sbrutus 	 * the dma copy may have to be broken up into multiple descriptors
10258e50dcc9Sbrutus 	 * since we can't cross a page boundary.
10268e50dcc9Sbrutus 	 */
10278e50dcc9Sbrutus 	ASSERT(cmd->dp_version == DCOPY_CMD_V0);
10288e50dcc9Sbrutus 	ASSERT(cmd->dp_cmd == DCOPY_CMD_COPY);
10298e50dcc9Sbrutus 	src_addr = cmd->dp.copy.cc_source;
10308e50dcc9Sbrutus 	dest_addr = cmd->dp.copy.cc_dest;
10318e50dcc9Sbrutus 	size = cmd->dp.copy.cc_size;
1032eca2601cSRandy Fishel 	priv->ip_start = ring->cr_desc_next;
10338e50dcc9Sbrutus 	while (size > 0) {
10348e50dcc9Sbrutus 		src_paddr = pa_to_ma(src_addr);
10358e50dcc9Sbrutus 		dest_paddr = pa_to_ma(dest_addr);
10368e50dcc9Sbrutus 
10378e50dcc9Sbrutus 		/* adjust for any offset into the page */
10388e50dcc9Sbrutus 		if ((src_addr & PAGEOFFSET) == 0) {
10398e50dcc9Sbrutus 			src_size = PAGESIZE;
10408e50dcc9Sbrutus 		} else {
10418e50dcc9Sbrutus 			src_size = PAGESIZE - (src_addr & PAGEOFFSET);
10428e50dcc9Sbrutus 		}
10438e50dcc9Sbrutus 		if ((dest_addr & PAGEOFFSET) == 0) {
10448e50dcc9Sbrutus 			dest_size = PAGESIZE;
10458e50dcc9Sbrutus 		} else {
10468e50dcc9Sbrutus 			dest_size = PAGESIZE - (dest_addr & PAGEOFFSET);
10478e50dcc9Sbrutus 		}
10488e50dcc9Sbrutus 
10498e50dcc9Sbrutus 		/* take the smallest of the three */
10508e50dcc9Sbrutus 		xfer_size = MIN(src_size, dest_size);
10518e50dcc9Sbrutus 		xfer_size = MIN(xfer_size, size);
10528e50dcc9Sbrutus 
10538e50dcc9Sbrutus 		/*
10548e50dcc9Sbrutus 		 * if this is the last descriptor, and we are supposed to
10558e50dcc9Sbrutus 		 * generate a completion, generate a completion. same logic
10568e50dcc9Sbrutus 		 * for interrupt.
10578e50dcc9Sbrutus 		 */
10588e50dcc9Sbrutus 		ctrl = 0;
1059eca2601cSRandy Fishel 		if (cmd->dp_flags & DCOPY_CMD_NOSRCSNP) {
1060eca2601cSRandy Fishel 			ctrl |= IOAT_DESC_CTRL_NOSRCSNP;
1061eca2601cSRandy Fishel 		}
1062eca2601cSRandy Fishel 		if (cmd->dp_flags & DCOPY_CMD_NODSTSNP) {
1063eca2601cSRandy Fishel 			ctrl |= IOAT_DESC_CTRL_NODSTSNP;
1064eca2601cSRandy Fishel 		}
10658e50dcc9Sbrutus 		if (xfer_size == size) {
10668e50dcc9Sbrutus 			if (!(cmd->dp_flags & DCOPY_CMD_NOSTAT)) {
10678e50dcc9Sbrutus 				ctrl |= IOAT_DESC_CTRL_CMPL;
10688e50dcc9Sbrutus 			}
10698e50dcc9Sbrutus 			if ((cmd->dp_flags & DCOPY_CMD_INTR)) {
10708e50dcc9Sbrutus 				ctrl |= IOAT_DESC_CTRL_INTR;
10718e50dcc9Sbrutus 			}
10728e50dcc9Sbrutus 		}
10738e50dcc9Sbrutus 
10748e50dcc9Sbrutus 		ioat_cmd_post_copy(ring, src_paddr, dest_paddr, xfer_size,
10758e50dcc9Sbrutus 		    ctrl);
10768e50dcc9Sbrutus 
10778e50dcc9Sbrutus 		/* go to the next page */
10788e50dcc9Sbrutus 		src_addr += xfer_size;
10798e50dcc9Sbrutus 		dest_addr += xfer_size;
10808e50dcc9Sbrutus 		size -= xfer_size;
10818e50dcc9Sbrutus 	}
10828e50dcc9Sbrutus 
1083eca2601cSRandy Fishel 	/* save away the state so we can poll on it. */
1084eca2601cSRandy Fishel 	priv->ip_generation = ring->cr_desc_gen_prev;
1085eca2601cSRandy Fishel 	priv->ip_index = ring->cr_desc_prev;
10868e50dcc9Sbrutus 
10878e50dcc9Sbrutus 	/* if queue not defined, tell the DMA engine about it */
10888e50dcc9Sbrutus 	if (!(cmd->dp_flags & DCOPY_CMD_QUEUE)) {
1089eca2601cSRandy Fishel 		/*
1090eca2601cSRandy Fishel 		 * Link the ring to a loop (currently only for FIPE).
1091eca2601cSRandy Fishel 		 */
1092eca2601cSRandy Fishel 		if (cmd->dp_flags & DCOPY_CMD_LOOP) {
1093eca2601cSRandy Fishel 			e = ioat_ring_loop(ring, cmd);
1094eca2601cSRandy Fishel 			if (e != DCOPY_SUCCESS) {
1095eca2601cSRandy Fishel 				mutex_exit(&ring->cr_desc_mutex);
1096eca2601cSRandy Fishel 				return (DCOPY_FAILURE);
1097eca2601cSRandy Fishel 			}
1098eca2601cSRandy Fishel 		}
1099eca2601cSRandy Fishel 
11008e50dcc9Sbrutus 		if (channel->ic_ver == IOAT_CBv1) {
11018e50dcc9Sbrutus 			ddi_put8(state->is_reg_handle,
11028e50dcc9Sbrutus 			    (uint8_t *)&channel->ic_regs[IOAT_V1_CHAN_CMD],
11038e50dcc9Sbrutus 			    0x2);
11048e50dcc9Sbrutus 		} else {
11058e50dcc9Sbrutus 			ASSERT(channel->ic_ver == IOAT_CBv2);
11068e50dcc9Sbrutus 			ddi_put16(state->is_reg_handle,
11078e50dcc9Sbrutus 			    (uint16_t *)&channel->ic_regs[IOAT_V2_CHAN_CNT],
11088e50dcc9Sbrutus 			    (uint16_t)(ring->cr_post_cnt & 0xFFFF));
11098e50dcc9Sbrutus 		}
11108e50dcc9Sbrutus 	}
11118e50dcc9Sbrutus 
11128e50dcc9Sbrutus 	mutex_exit(&ring->cr_desc_mutex);
11138e50dcc9Sbrutus 
11148e50dcc9Sbrutus 	return (DCOPY_SUCCESS);
11158e50dcc9Sbrutus }
11168e50dcc9Sbrutus 
11178e50dcc9Sbrutus 
11188e50dcc9Sbrutus /*
11198e50dcc9Sbrutus  * ioat_cmd_post_dca()
11208e50dcc9Sbrutus  */
11218e50dcc9Sbrutus static void
ioat_cmd_post_dca(ioat_channel_ring_t * ring,uint32_t dca_id)11228e50dcc9Sbrutus ioat_cmd_post_dca(ioat_channel_ring_t *ring, uint32_t dca_id)
11238e50dcc9Sbrutus {
11245aefaa16SMark Johnson 	ioat_chan_dca_desc_t *saved_prev;
11258e50dcc9Sbrutus 	ioat_chan_dca_desc_t *desc;
11268e50dcc9Sbrutus 	ioat_chan_dca_desc_t *prev;
11278e50dcc9Sbrutus 	ioat_channel_t channel;
11285aefaa16SMark Johnson 	uint64_t next_desc_phys;
11295aefaa16SMark Johnson 	off_t prev_offset;
11305aefaa16SMark Johnson 	off_t next_offset;
11318e50dcc9Sbrutus 
11328e50dcc9Sbrutus 
11338e50dcc9Sbrutus 	channel = ring->cr_chan;
11348e50dcc9Sbrutus 	desc = (ioat_chan_dca_desc_t *)&ring->cr_desc[ring->cr_desc_next];
11358e50dcc9Sbrutus 	prev = (ioat_chan_dca_desc_t *)&ring->cr_desc[ring->cr_desc_prev];
11368e50dcc9Sbrutus 
11378e50dcc9Sbrutus 	/* keep track of the number of descs posted for cbv2 */
11388e50dcc9Sbrutus 	ring->cr_post_cnt++;
11398e50dcc9Sbrutus 
11408e50dcc9Sbrutus 	/*
11418e50dcc9Sbrutus 	 * post a context change desriptor. If dca has never been used on
11428e50dcc9Sbrutus 	 * this channel, or if the id doesn't match the last id used on this
11438e50dcc9Sbrutus 	 * channel, set CONTEXT_CHANGE bit and dca id, set dca state to active,
11448e50dcc9Sbrutus 	 * and save away the id we're using.
11458e50dcc9Sbrutus 	 */
11468e50dcc9Sbrutus 	desc->dd_ctrl = IOAT_DESC_CTRL_OP_CNTX;
11478e50dcc9Sbrutus 	desc->dd_next_desc = 0x0;
11488e50dcc9Sbrutus 	if (!channel->ic_dca_active || (channel->ic_dca_current != dca_id)) {
11498e50dcc9Sbrutus 		channel->ic_dca_active = B_TRUE;
11508e50dcc9Sbrutus 		channel->ic_dca_current = dca_id;
11518e50dcc9Sbrutus 		desc->dd_ctrl |= IOAT_DESC_CTRL_CNTX_CHNG;
11528e50dcc9Sbrutus 		desc->dd_cntx = dca_id;
11538e50dcc9Sbrutus 	}
11548e50dcc9Sbrutus 
11555aefaa16SMark Johnson 	/*
11565aefaa16SMark Johnson 	 * save next desc and prev offset for when we link the two
11575aefaa16SMark Johnson 	 * descriptors together.
11585aefaa16SMark Johnson 	 */
11595aefaa16SMark Johnson 	saved_prev = prev;
11605aefaa16SMark Johnson 	prev_offset = ring->cr_desc_prev << 6;
11615aefaa16SMark Johnson 	next_offset = ring->cr_desc_next << 6;
11625aefaa16SMark Johnson 	next_desc_phys = ring->cr_phys_desc + next_offset;
11638e50dcc9Sbrutus 
11648e50dcc9Sbrutus 	/* save the current desc_next and desc_last for the completion */
11658e50dcc9Sbrutus 	ring->cr_desc_prev = ring->cr_desc_next;
11668e50dcc9Sbrutus 	ring->cr_desc_gen_prev = ring->cr_desc_gen;
11678e50dcc9Sbrutus 
11688e50dcc9Sbrutus 	/* increment next/gen so it points to the next free desc */
11698e50dcc9Sbrutus 	ring->cr_desc_next++;
11708e50dcc9Sbrutus 	if (ring->cr_desc_next > ring->cr_desc_last) {
11718e50dcc9Sbrutus 		ring->cr_desc_next = 0;
11728e50dcc9Sbrutus 		ring->cr_desc_gen++;
11738e50dcc9Sbrutus 	}
11748e50dcc9Sbrutus 
11758e50dcc9Sbrutus 	/*
11768e50dcc9Sbrutus 	 * if this is CBv2, link the descriptor to an empty descriptor. Since
11778e50dcc9Sbrutus 	 * we always leave on desc empty to detect full, this works out.
11788e50dcc9Sbrutus 	 */
11798e50dcc9Sbrutus 	if (ring->cr_chan->ic_ver == IOAT_CBv2) {
11808e50dcc9Sbrutus 		desc = (ioat_chan_dca_desc_t *)
11818e50dcc9Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
11828e50dcc9Sbrutus 		prev = (ioat_chan_dca_desc_t *)
11838e50dcc9Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
11848e50dcc9Sbrutus 		desc->dd_ctrl = 0;
11858e50dcc9Sbrutus 		desc->dd_next_desc = 0x0;
11865aefaa16SMark Johnson 		(void) ddi_dma_sync(channel->ic_desc_dma_handle,
11875aefaa16SMark Johnson 		    ring->cr_desc_next << 6, 64, DDI_DMA_SYNC_FORDEV);
11888e50dcc9Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
11898e50dcc9Sbrutus 		    (ring->cr_desc_next << 6);
11908e50dcc9Sbrutus 	}
11915aefaa16SMark Johnson 
11925aefaa16SMark Johnson 	/* Put the descriptors physical address in the previous descriptor */
11935aefaa16SMark Johnson 	/*LINTED:E_TRUE_LOGICAL_EXPR*/
11945aefaa16SMark Johnson 	ASSERT(sizeof (ioat_chan_dca_desc_t) == 64);
11955aefaa16SMark Johnson 
11965aefaa16SMark Johnson 	/* sync the current desc */
11975aefaa16SMark Johnson 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, next_offset, 64,
11985aefaa16SMark Johnson 	    DDI_DMA_SYNC_FORDEV);
11995aefaa16SMark Johnson 
12005aefaa16SMark Johnson 	/* update the previous desc and sync it too */
12015aefaa16SMark Johnson 	saved_prev->dd_next_desc = next_desc_phys;
12025aefaa16SMark Johnson 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, prev_offset, 64,
12035aefaa16SMark Johnson 	    DDI_DMA_SYNC_FORDEV);
12048e50dcc9Sbrutus }
12058e50dcc9Sbrutus 
12068e50dcc9Sbrutus 
12078e50dcc9Sbrutus /*
12088e50dcc9Sbrutus  * ioat_cmd_post_copy()
12098e50dcc9Sbrutus  *
12108e50dcc9Sbrutus  */
12118e50dcc9Sbrutus 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)12128e50dcc9Sbrutus ioat_cmd_post_copy(ioat_channel_ring_t *ring, uint64_t src_addr,
12138e50dcc9Sbrutus     uint64_t dest_addr, uint32_t size, uint32_t ctrl)
12148e50dcc9Sbrutus {
12155aefaa16SMark Johnson 	ioat_chan_dma_desc_t *saved_prev;
12168e50dcc9Sbrutus 	ioat_chan_dma_desc_t *desc;
12178e50dcc9Sbrutus 	ioat_chan_dma_desc_t *prev;
12188e50dcc9Sbrutus 	ioat_channel_t channel;
12195aefaa16SMark Johnson 	uint64_t next_desc_phy;
12205aefaa16SMark Johnson 	off_t prev_offset;
12215aefaa16SMark Johnson 	off_t next_offset;
12228e50dcc9Sbrutus 
12238e50dcc9Sbrutus 
12248e50dcc9Sbrutus 	channel = ring->cr_chan;
12258e50dcc9Sbrutus 	desc = (ioat_chan_dma_desc_t *)&ring->cr_desc[ring->cr_desc_next];
12268e50dcc9Sbrutus 	prev = (ioat_chan_dma_desc_t *)&ring->cr_desc[ring->cr_desc_prev];
12278e50dcc9Sbrutus 
12288e50dcc9Sbrutus 	/* keep track of the number of descs posted for cbv2 */
12298e50dcc9Sbrutus 	ring->cr_post_cnt++;
12308e50dcc9Sbrutus 
12318e50dcc9Sbrutus 	/* write in the DMA desc */
12328e50dcc9Sbrutus 	desc->dd_ctrl = IOAT_DESC_CTRL_OP_DMA | ctrl;
12338e50dcc9Sbrutus 	desc->dd_size = size;
12348e50dcc9Sbrutus 	desc->dd_src_paddr = src_addr;
12358e50dcc9Sbrutus 	desc->dd_dest_paddr = dest_addr;
12368e50dcc9Sbrutus 	desc->dd_next_desc = 0x0;
12378e50dcc9Sbrutus 
12385aefaa16SMark Johnson 	/*
12395aefaa16SMark Johnson 	 * save next desc and prev offset for when we link the two
12405aefaa16SMark Johnson 	 * descriptors together.
12415aefaa16SMark Johnson 	 */
12425aefaa16SMark Johnson 	saved_prev = prev;
12435aefaa16SMark Johnson 	prev_offset = ring->cr_desc_prev << 6;
12445aefaa16SMark Johnson 	next_offset = ring->cr_desc_next << 6;
12455aefaa16SMark Johnson 	next_desc_phy = ring->cr_phys_desc + next_offset;
12468e50dcc9Sbrutus 
12478e50dcc9Sbrutus 	/* increment next/gen so it points to the next free desc */
12488e50dcc9Sbrutus 	ring->cr_desc_prev = ring->cr_desc_next;
12498e50dcc9Sbrutus 	ring->cr_desc_gen_prev = ring->cr_desc_gen;
12508e50dcc9Sbrutus 
12518e50dcc9Sbrutus 	/* increment next/gen so it points to the next free desc */
12528e50dcc9Sbrutus 	ring->cr_desc_next++;
12538e50dcc9Sbrutus 	if (ring->cr_desc_next > ring->cr_desc_last) {
12548e50dcc9Sbrutus 		ring->cr_desc_next = 0;
12558e50dcc9Sbrutus 		ring->cr_desc_gen++;
12568e50dcc9Sbrutus 	}
12578e50dcc9Sbrutus 
12588e50dcc9Sbrutus 	/*
12598e50dcc9Sbrutus 	 * if this is CBv2, link the descriptor to an empty descriptor. Since
12608e50dcc9Sbrutus 	 * we always leave on desc empty to detect full, this works out.
12618e50dcc9Sbrutus 	 */
12628e50dcc9Sbrutus 	if (ring->cr_chan->ic_ver == IOAT_CBv2) {
12638e50dcc9Sbrutus 		desc = (ioat_chan_dma_desc_t *)
12648e50dcc9Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
12658e50dcc9Sbrutus 		prev = (ioat_chan_dma_desc_t *)
12668e50dcc9Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
12678e50dcc9Sbrutus 		desc->dd_size = 0;
12688e50dcc9Sbrutus 		desc->dd_ctrl = 0;
12698e50dcc9Sbrutus 		desc->dd_next_desc = 0x0;
12705aefaa16SMark Johnson 		(void) ddi_dma_sync(channel->ic_desc_dma_handle,
12715aefaa16SMark Johnson 		    ring->cr_desc_next << 6, 64, DDI_DMA_SYNC_FORDEV);
12728e50dcc9Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
12738e50dcc9Sbrutus 		    (ring->cr_desc_next << 6);
12748e50dcc9Sbrutus 	}
12755aefaa16SMark Johnson 
12765aefaa16SMark Johnson 	/* Put the descriptors physical address in the previous descriptor */
12775aefaa16SMark Johnson 	/*LINTED:E_TRUE_LOGICAL_EXPR*/
12785aefaa16SMark Johnson 	ASSERT(sizeof (ioat_chan_dma_desc_t) == 64);
12795aefaa16SMark Johnson 
12805aefaa16SMark Johnson 	/* sync the current desc */
12815aefaa16SMark Johnson 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, next_offset, 64,
12825aefaa16SMark Johnson 	    DDI_DMA_SYNC_FORDEV);
12835aefaa16SMark Johnson 
12845aefaa16SMark Johnson 	/* update the previous desc and sync it too */
12855aefaa16SMark Johnson 	saved_prev->dd_next_desc = next_desc_phy;
12865aefaa16SMark Johnson 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, prev_offset, 64,
12875aefaa16SMark Johnson 	    DDI_DMA_SYNC_FORDEV);
12888e50dcc9Sbrutus }
12898e50dcc9Sbrutus 
12908e50dcc9Sbrutus 
12918e50dcc9Sbrutus /*
12928e50dcc9Sbrutus  * ioat_cmd_poll()
12938e50dcc9Sbrutus  */
12948e50dcc9Sbrutus int
ioat_cmd_poll(void * private,dcopy_cmd_t cmd)12958e50dcc9Sbrutus ioat_cmd_poll(void *private, dcopy_cmd_t cmd)
12968e50dcc9Sbrutus {
12978e50dcc9Sbrutus 	ioat_channel_ring_t *ring;
12988e50dcc9Sbrutus 	ioat_cmd_private_t *priv;
12998e50dcc9Sbrutus 	ioat_channel_t channel;
13008e50dcc9Sbrutus 	uint64_t generation;
13018e50dcc9Sbrutus 	uint64_t last_cmpl;
13028e50dcc9Sbrutus 
1303eca2601cSRandy Fishel 	ASSERT(cmd != NULL);
13048e50dcc9Sbrutus 	channel = (ioat_channel_t)private;
13058e50dcc9Sbrutus 	priv = cmd->dp_private->pr_device_cmd_private;
13068e50dcc9Sbrutus 
13078e50dcc9Sbrutus 	ring = channel->ic_ring;
13088e50dcc9Sbrutus 	ASSERT(ring != NULL);
13098e50dcc9Sbrutus 
1310eca2601cSRandy Fishel 	if ((cmd->dp_flags & DCOPY_CMD_NOWAIT) == 0) {
1311eca2601cSRandy Fishel 		mutex_enter(&ring->cr_cmpl_mutex);
1312eca2601cSRandy Fishel 
1313eca2601cSRandy Fishel 	/*
1314eca2601cSRandy Fishel 	 * Try to acquire mutex if NOWAIT flag is set.
1315eca2601cSRandy Fishel 	 * Return failure if failed to acquire mutex.
1316eca2601cSRandy Fishel 	 */
1317eca2601cSRandy Fishel 	} else if (mutex_tryenter(&ring->cr_cmpl_mutex) == 0) {
1318eca2601cSRandy Fishel 		return (DCOPY_FAILURE);
1319eca2601cSRandy Fishel 	}
13208e50dcc9Sbrutus 
13218e50dcc9Sbrutus 	/* if the channel had a fatal failure, fail all polls */
13228e50dcc9Sbrutus 	if ((channel->ic_channel_state == IOAT_CHANNEL_IN_FAILURE) ||
13238e50dcc9Sbrutus 	    IOAT_CMPL_FAILED(channel)) {
13248e50dcc9Sbrutus 		mutex_exit(&ring->cr_cmpl_mutex);
13258e50dcc9Sbrutus 		return (DCOPY_FAILURE);
13268e50dcc9Sbrutus 	}
13278e50dcc9Sbrutus 
13288e50dcc9Sbrutus 	/*
13298e50dcc9Sbrutus 	 * if the current completion is the same as the last time we read one,
13308e50dcc9Sbrutus 	 * post is still pending, nothing further to do. We track completions
13318e50dcc9Sbrutus 	 * as indexes into the ring since post uses VAs and the H/W returns
13328e50dcc9Sbrutus 	 * PAs. We grab a snapshot of generation and last_cmpl in the mutex.
13338e50dcc9Sbrutus 	 */
13348e50dcc9Sbrutus 	(void) ddi_dma_sync(channel->ic_cmpl_dma_handle, 0, 0,
13358e50dcc9Sbrutus 	    DDI_DMA_SYNC_FORCPU);
13368e50dcc9Sbrutus 	last_cmpl = IOAT_CMPL_INDEX(channel);
13378e50dcc9Sbrutus 	if (last_cmpl != ring->cr_cmpl_last) {
13388e50dcc9Sbrutus 		/*
13398e50dcc9Sbrutus 		 * if we wrapped the ring, increment the generation. Store
13408e50dcc9Sbrutus 		 * the last cmpl. This logic assumes a physically contiguous
13418e50dcc9Sbrutus 		 * ring.
13428e50dcc9Sbrutus 		 */
13438e50dcc9Sbrutus 		if (last_cmpl < ring->cr_cmpl_last) {
13448e50dcc9Sbrutus 			ring->cr_cmpl_gen++;
13458e50dcc9Sbrutus 		}
13468e50dcc9Sbrutus 		ring->cr_cmpl_last = last_cmpl;
13478e50dcc9Sbrutus 		generation = ring->cr_cmpl_gen;
13488e50dcc9Sbrutus 
13498e50dcc9Sbrutus 	} else {
13508e50dcc9Sbrutus 		generation = ring->cr_cmpl_gen;
13518e50dcc9Sbrutus 	}
13528e50dcc9Sbrutus 
13538e50dcc9Sbrutus 	mutex_exit(&ring->cr_cmpl_mutex);
13548e50dcc9Sbrutus 
13558e50dcc9Sbrutus 	/*
13568e50dcc9Sbrutus 	 * if cmd isn't passed in, well return.  Useful for updating the
13578e50dcc9Sbrutus 	 * consumer pointer (ring->cr_cmpl_last).
13588e50dcc9Sbrutus 	 */
1359eca2601cSRandy Fishel 	if (cmd->dp_flags & DCOPY_CMD_SYNC) {
13608e50dcc9Sbrutus 		return (DCOPY_PENDING);
13618e50dcc9Sbrutus 	}
13628e50dcc9Sbrutus 
13638e50dcc9Sbrutus 	/*
13648e50dcc9Sbrutus 	 * if the post's generation is old, this post has completed. No reason
13658e50dcc9Sbrutus 	 * to go check the last completion. if the generation is the same
13668e50dcc9Sbrutus 	 * and if the post is before or = to the last completion processed,
13678e50dcc9Sbrutus 	 * the post has completed.
13688e50dcc9Sbrutus 	 */
13698e50dcc9Sbrutus 	if (priv->ip_generation < generation) {
13708e50dcc9Sbrutus 		return (DCOPY_COMPLETED);
13718e50dcc9Sbrutus 	} else if ((priv->ip_generation == generation) &&
13728e50dcc9Sbrutus 	    (priv->ip_index <= last_cmpl)) {
13738e50dcc9Sbrutus 		return (DCOPY_COMPLETED);
13748e50dcc9Sbrutus 	}
13758e50dcc9Sbrutus 
13768e50dcc9Sbrutus 	return (DCOPY_PENDING);
13778e50dcc9Sbrutus }
13788e50dcc9Sbrutus 
13798e50dcc9Sbrutus 
13808e50dcc9Sbrutus /*
13818e50dcc9Sbrutus  * ioat_ring_reserve()
13828e50dcc9Sbrutus  */
13838e50dcc9Sbrutus int
ioat_ring_reserve(ioat_channel_t channel,ioat_channel_ring_t * ring,dcopy_cmd_t cmd)13848e50dcc9Sbrutus ioat_ring_reserve(ioat_channel_t channel, ioat_channel_ring_t *ring,
13858e50dcc9Sbrutus     dcopy_cmd_t cmd)
13868e50dcc9Sbrutus {
13878e50dcc9Sbrutus 	uint64_t dest_addr;
13888e50dcc9Sbrutus 	uint32_t dest_size;
13898e50dcc9Sbrutus 	uint64_t src_addr;
13908e50dcc9Sbrutus 	uint32_t src_size;
13918e50dcc9Sbrutus 	size_t xfer_size;
13928e50dcc9Sbrutus 	uint64_t desc;
13938e50dcc9Sbrutus 	int num_desc;
13948e50dcc9Sbrutus 	size_t size;
13958e50dcc9Sbrutus 	int i;
13968e50dcc9Sbrutus 
13978e50dcc9Sbrutus 
13988e50dcc9Sbrutus 	/*
13998e50dcc9Sbrutus 	 * figure out how many descriptors we need. This can include a dca
14008e50dcc9Sbrutus 	 * desc and multiple desc for a dma copy.
14018e50dcc9Sbrutus 	 */
14028e50dcc9Sbrutus 	num_desc = 0;
14038e50dcc9Sbrutus 	if ((channel->ic_ver == IOAT_CBv2) &&
14048e50dcc9Sbrutus 	    (cmd->dp_flags & DCOPY_CMD_DCA)) {
14058e50dcc9Sbrutus 		num_desc++;
14068e50dcc9Sbrutus 	}
14078e50dcc9Sbrutus 	src_addr = cmd->dp.copy.cc_source;
14088e50dcc9Sbrutus 	dest_addr = cmd->dp.copy.cc_dest;
14098e50dcc9Sbrutus 	size = cmd->dp.copy.cc_size;
14108e50dcc9Sbrutus 	while (size > 0) {
14118e50dcc9Sbrutus 		num_desc++;
14128e50dcc9Sbrutus 
14138e50dcc9Sbrutus 		/* adjust for any offset into the page */
14148e50dcc9Sbrutus 		if ((src_addr & PAGEOFFSET) == 0) {
14158e50dcc9Sbrutus 			src_size = PAGESIZE;
14168e50dcc9Sbrutus 		} else {
14178e50dcc9Sbrutus 			src_size = PAGESIZE - (src_addr & PAGEOFFSET);
14188e50dcc9Sbrutus 		}
14198e50dcc9Sbrutus 		if ((dest_addr & PAGEOFFSET) == 0) {
14208e50dcc9Sbrutus 			dest_size = PAGESIZE;
14218e50dcc9Sbrutus 		} else {
14228e50dcc9Sbrutus 			dest_size = PAGESIZE - (dest_addr & PAGEOFFSET);
14238e50dcc9Sbrutus 		}
14248e50dcc9Sbrutus 
14258e50dcc9Sbrutus 		/* take the smallest of the three */
14268e50dcc9Sbrutus 		xfer_size = MIN(src_size, dest_size);
14278e50dcc9Sbrutus 		xfer_size = MIN(xfer_size, size);
14288e50dcc9Sbrutus 
14298e50dcc9Sbrutus 		/* go to the next page */
14308e50dcc9Sbrutus 		src_addr += xfer_size;
14318e50dcc9Sbrutus 		dest_addr += xfer_size;
14328e50dcc9Sbrutus 		size -= xfer_size;
14338e50dcc9Sbrutus 	}
14348e50dcc9Sbrutus 
14358e50dcc9Sbrutus 	/* Make sure we have space for these descriptors */
14368e50dcc9Sbrutus 	desc = ring->cr_desc_next;
14378e50dcc9Sbrutus 	for (i = 0; i < num_desc; i++) {
14388e50dcc9Sbrutus 
14398e50dcc9Sbrutus 		/*
14408e50dcc9Sbrutus 		 * if this is the last descriptor in the ring, see if the
14418e50dcc9Sbrutus 		 * last completed descriptor is #0.
14428e50dcc9Sbrutus 		 */
14438e50dcc9Sbrutus 		if (desc == ring->cr_desc_last) {
14448e50dcc9Sbrutus 			if (ring->cr_cmpl_last == 0) {
14458e50dcc9Sbrutus 				/*
14468e50dcc9Sbrutus 				 * if we think the ring is full, update where
14478e50dcc9Sbrutus 				 * the H/W really is and check for full again.
14488e50dcc9Sbrutus 				 */
1449eca2601cSRandy Fishel 				cmd->dp_flags |= DCOPY_CMD_SYNC;
1450eca2601cSRandy Fishel 				(void) ioat_cmd_poll(channel, cmd);
1451eca2601cSRandy Fishel 				cmd->dp_flags &= ~DCOPY_CMD_SYNC;
14528e50dcc9Sbrutus 				if (ring->cr_cmpl_last == 0) {
14538e50dcc9Sbrutus 					return (DCOPY_NORESOURCES);
14548e50dcc9Sbrutus 				}
14558e50dcc9Sbrutus 			}
14568e50dcc9Sbrutus 
14578e50dcc9Sbrutus 			/*
14588e50dcc9Sbrutus 			 * go to the next descriptor which is zero in this
14598e50dcc9Sbrutus 			 * case.
14608e50dcc9Sbrutus 			 */
14618e50dcc9Sbrutus 			desc = 0;
14628e50dcc9Sbrutus 
14638e50dcc9Sbrutus 		/*
14648e50dcc9Sbrutus 		 * if this is not the last descriptor in the ring, see if
14658e50dcc9Sbrutus 		 * the last completion we saw was the next descriptor.
14668e50dcc9Sbrutus 		 */
14678e50dcc9Sbrutus 		} else {
14688e50dcc9Sbrutus 			if ((desc + 1) == ring->cr_cmpl_last) {
14698e50dcc9Sbrutus 				/*
14708e50dcc9Sbrutus 				 * if we think the ring is full, update where
14718e50dcc9Sbrutus 				 * the H/W really is and check for full again.
14728e50dcc9Sbrutus 				 */
1473eca2601cSRandy Fishel 				cmd->dp_flags |= DCOPY_CMD_SYNC;
1474eca2601cSRandy Fishel 				(void) ioat_cmd_poll(channel, cmd);
1475eca2601cSRandy Fishel 				cmd->dp_flags &= ~DCOPY_CMD_SYNC;
14768e50dcc9Sbrutus 				if ((desc + 1) == ring->cr_cmpl_last) {
14778e50dcc9Sbrutus 					return (DCOPY_NORESOURCES);
14788e50dcc9Sbrutus 				}
14798e50dcc9Sbrutus 			}
14808e50dcc9Sbrutus 
14818e50dcc9Sbrutus 			/* go to the next descriptor */
14828e50dcc9Sbrutus 			desc++;
14838e50dcc9Sbrutus 		}
14848e50dcc9Sbrutus 	}
14858e50dcc9Sbrutus 
14868e50dcc9Sbrutus 	return (DCOPY_SUCCESS);
14878e50dcc9Sbrutus }
1488