xref: /onnv-gate/usr/src/uts/i86pc/io/ioat/ioat_chan.c (revision 9628:0496701c44c8)
16707Sbrutus /*
26707Sbrutus  * CDDL HEADER START
36707Sbrutus  *
46707Sbrutus  * The contents of this file are subject to the terms of the
56707Sbrutus  * Common Development and Distribution License (the "License").
66707Sbrutus  * You may not use this file except in compliance with the License.
76707Sbrutus  *
86707Sbrutus  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96707Sbrutus  * or http://www.opensolaris.org/os/licensing.
106707Sbrutus  * See the License for the specific language governing permissions
116707Sbrutus  * and limitations under the License.
126707Sbrutus  *
136707Sbrutus  * When distributing Covered Code, include this CDDL HEADER in each
146707Sbrutus  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156707Sbrutus  * If applicable, add the following below this CDDL HEADER, with the
166707Sbrutus  * fields enclosed by brackets "[]" replaced with your own identifying
176707Sbrutus  * information: Portions Copyright [yyyy] [name of copyright owner]
186707Sbrutus  *
196707Sbrutus  * CDDL HEADER END
206707Sbrutus  */
216707Sbrutus 
226707Sbrutus /*
239064SMark.Johnson@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
246707Sbrutus  * Use is subject to license terms.
256707Sbrutus  */
266707Sbrutus 
276707Sbrutus #include <sys/errno.h>
286707Sbrutus #include <sys/types.h>
296707Sbrutus #include <sys/conf.h>
306707Sbrutus #include <sys/kmem.h>
316707Sbrutus #include <sys/ddi.h>
326707Sbrutus #include <sys/stat.h>
336707Sbrutus #include <sys/sunddi.h>
346707Sbrutus #include <sys/file.h>
356707Sbrutus #include <sys/open.h>
366707Sbrutus #include <sys/modctl.h>
376707Sbrutus #include <sys/ddi_impldefs.h>
386707Sbrutus #include <sys/sysmacros.h>
396707Sbrutus #include <vm/hat.h>
406707Sbrutus #include <vm/as.h>
416707Sbrutus #include <sys/mach_mmu.h>
426707Sbrutus #ifdef __xpv
436707Sbrutus #include <sys/hypervisor.h>
446707Sbrutus #endif
456707Sbrutus 
466707Sbrutus #include <sys/ioat.h>
476707Sbrutus 
486707Sbrutus 
496707Sbrutus extern ddi_device_acc_attr_t ioat_acc_attr;
506707Sbrutus 
516707Sbrutus /* dma attr for the descriptor rings */
526707Sbrutus ddi_dma_attr_t ioat_desc_dma_attr = {
536707Sbrutus 	DMA_ATTR_V0,		/* dma_attr_version */
546707Sbrutus 	0x0,			/* dma_attr_addr_lo */
556707Sbrutus 	0xffffffffffffffff,	/* dma_attr_addr_hi */
566707Sbrutus 	0xffffffff,		/* dma_attr_count_max */
576707Sbrutus 	0x1000,			/* dma_attr_align */
586707Sbrutus 	0x1,			/* dma_attr_burstsizes */
596707Sbrutus 	0x1,			/* dma_attr_minxfer */
606707Sbrutus 	0xffffffff,		/* dma_attr_maxxfer */
616707Sbrutus 	0xffffffff,		/* dma_attr_seg */
626707Sbrutus 	0x1,			/* dma_attr_sgllen */
636707Sbrutus 	0x1,			/* dma_attr_granular */
646707Sbrutus 	0x0,			/* dma_attr_flags */
656707Sbrutus };
666707Sbrutus 
676707Sbrutus /* dma attr for the completion buffers */
686707Sbrutus ddi_dma_attr_t ioat_cmpl_dma_attr = {
696707Sbrutus 	DMA_ATTR_V0,		/* dma_attr_version */
706707Sbrutus 	0x0,			/* dma_attr_addr_lo */
716707Sbrutus 	0xffffffffffffffff,	/* dma_attr_addr_hi */
726707Sbrutus 	0xffffffff,		/* dma_attr_count_max */
736707Sbrutus 	0x40,			/* dma_attr_align */
746707Sbrutus 	0x1,			/* dma_attr_burstsizes */
756707Sbrutus 	0x1,			/* dma_attr_minxfer */
766707Sbrutus 	0xffffffff,		/* dma_attr_maxxfer */
776707Sbrutus 	0xffffffff,		/* dma_attr_seg */
786707Sbrutus 	0x1,			/* dma_attr_sgllen */
796707Sbrutus 	0x1,			/* dma_attr_granular */
806707Sbrutus 	0x0,			/* dma_attr_flags */
816707Sbrutus };
826707Sbrutus 
836707Sbrutus static int ioat_completion_alloc(ioat_channel_t channel);
846707Sbrutus static void ioat_completion_free(ioat_channel_t channel);
856707Sbrutus static void ioat_channel_start(ioat_channel_t channel);
866707Sbrutus static void ioat_channel_reset(ioat_channel_t channel);
876707Sbrutus 
886707Sbrutus int ioat_ring_alloc(ioat_channel_t channel, uint_t desc_cnt);
896707Sbrutus void ioat_ring_free(ioat_channel_t channel);
906707Sbrutus void ioat_ring_seed(ioat_channel_t channel, ioat_chan_dma_desc_t *desc);
916707Sbrutus int ioat_ring_reserve(ioat_channel_t channel, ioat_channel_ring_t *ring,
926707Sbrutus     dcopy_cmd_t cmd);
936707Sbrutus 
946707Sbrutus static void ioat_cmd_post_copy(ioat_channel_ring_t *ring, uint64_t src_addr,
956707Sbrutus     uint64_t dest_addr, uint32_t size, uint32_t ctrl);
966707Sbrutus static void ioat_cmd_post_dca(ioat_channel_ring_t *ring, uint32_t dca_id);
976707Sbrutus 
986707Sbrutus 
996707Sbrutus /*
1006707Sbrutus  * ioat_channel_init()
1016707Sbrutus  */
1026707Sbrutus int
1036707Sbrutus ioat_channel_init(ioat_state_t *state)
1046707Sbrutus {
1056707Sbrutus 	int i;
1066707Sbrutus 
1076707Sbrutus 	/*
1086707Sbrutus 	 * initialize each dma channel's state which doesn't change across
1096707Sbrutus 	 * channel alloc/free.
1106707Sbrutus 	 */
1116707Sbrutus 	state->is_chansize = sizeof (struct ioat_channel_s) *
1126707Sbrutus 	    state->is_num_channels;
1136707Sbrutus 	state->is_channel = kmem_zalloc(state->is_chansize, KM_SLEEP);
1146707Sbrutus 	for (i = 0; i < state->is_num_channels; i++) {
1156707Sbrutus 		state->is_channel[i].ic_state = state;
1166707Sbrutus 		state->is_channel[i].ic_regs = (uint8_t *)
1176707Sbrutus 		    ((uintptr_t)state->is_genregs +
1186707Sbrutus 		    (uintptr_t)(IOAT_CHANNELREG_OFFSET * (i + 1)));
1196707Sbrutus 	}
1206707Sbrutus 
1216707Sbrutus 	/* initial the allocator (from 0 to state->is_num_channels) */
1226707Sbrutus 	ioat_rs_init(state, 0, state->is_num_channels, &state->is_channel_rs);
1236707Sbrutus 
1246707Sbrutus 	return (DDI_SUCCESS);
1256707Sbrutus }
1266707Sbrutus 
1276707Sbrutus 
1286707Sbrutus /*
1296707Sbrutus  * ioat_channel_fini()
1306707Sbrutus  */
1316707Sbrutus void
1326707Sbrutus ioat_channel_fini(ioat_state_t *state)
1336707Sbrutus {
1346707Sbrutus 	ioat_rs_fini(&state->is_channel_rs);
1356707Sbrutus 	kmem_free(state->is_channel, state->is_chansize);
1366707Sbrutus }
1376707Sbrutus 
1386707Sbrutus 
1396707Sbrutus /*
1406707Sbrutus  * ioat_channel_alloc()
1416707Sbrutus  *   NOTE: We intentionaly don't handle DCOPY_SLEEP (if no channels are
1426707Sbrutus  *	available)
1436707Sbrutus  */
1446707Sbrutus /*ARGSUSED*/
1456707Sbrutus int
1466707Sbrutus ioat_channel_alloc(void *device_private, dcopy_handle_t handle, int flags,
1476707Sbrutus     uint_t size, dcopy_query_channel_t *info, void *channel_private)
1486707Sbrutus {
1496707Sbrutus #define	CHANSTRSIZE	20
1506707Sbrutus 	struct ioat_channel_s *channel;
1516707Sbrutus 	char chanstr[CHANSTRSIZE];
1526707Sbrutus 	ioat_channel_t *chan;
1536707Sbrutus 	ioat_state_t *state;
1546707Sbrutus 	size_t cmd_size;
1556707Sbrutus 	uint_t chan_num;
1566707Sbrutus 	uint32_t estat;
1576707Sbrutus 	int e;
1586707Sbrutus 
1596707Sbrutus 
1606707Sbrutus 	state = (ioat_state_t *)device_private;
1616707Sbrutus 	chan = (ioat_channel_t *)channel_private;
1626707Sbrutus 
1636707Sbrutus 	/* allocate a H/W channel */
1646707Sbrutus 	e = ioat_rs_alloc(state->is_channel_rs, &chan_num);
1656707Sbrutus 	if (e != DDI_SUCCESS) {
1666707Sbrutus 		return (DCOPY_NORESOURCES);
1676707Sbrutus 	}
1686707Sbrutus 
1696707Sbrutus 	channel = &state->is_channel[chan_num];
1706707Sbrutus 	channel->ic_inuse = B_TRUE;
1716707Sbrutus 	channel->ic_chan_num = chan_num;
1726707Sbrutus 	channel->ic_ver = state->is_ver;
1736707Sbrutus 	channel->ic_dca_active = B_FALSE;
1746707Sbrutus 	channel->ic_channel_state = IOAT_CHANNEL_OK;
1756707Sbrutus 	channel->ic_dcopy_handle = handle;
1766707Sbrutus 
1776707Sbrutus #ifdef	DEBUG
1786707Sbrutus 	{
1796707Sbrutus 		/* if we're cbv2, verify that the V2 compatibility bit is set */
1806707Sbrutus 		uint16_t reg;
1816707Sbrutus 		if (channel->ic_ver == IOAT_CBv2) {
1826707Sbrutus 			reg = ddi_get16(state->is_reg_handle,
1836707Sbrutus 			    (uint16_t *)&channel->ic_regs[IOAT_CHAN_COMP]);
1846707Sbrutus 			ASSERT(reg & 0x2);
1856707Sbrutus 		}
1866707Sbrutus 	}
1876707Sbrutus #endif
1886707Sbrutus 
1896707Sbrutus 	/*
1906707Sbrutus 	 * Configure DMA channel
1916707Sbrutus 	 *   Channel In Use
1926707Sbrutus 	 *   Error Interrupt Enable
1936707Sbrutus 	 *   Any Error Abort Enable
1946707Sbrutus 	 *   Error Completion Enable
1956707Sbrutus 	 */
1966707Sbrutus 	ddi_put16(state->is_reg_handle,
1976707Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x011C);
1986707Sbrutus 
1996707Sbrutus 	/* check channel error register, clear any errors */
2006707Sbrutus 	estat = ddi_get32(state->is_reg_handle,
2016707Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
2026707Sbrutus 	if (estat != 0) {
2036707Sbrutus #ifdef	DEBUG
2046707Sbrutus 		cmn_err(CE_CONT, "cleared errors (0x%x) before channel (%d) "
2056707Sbrutus 		    "enable\n", estat, channel->ic_chan_num);
2066707Sbrutus #endif
2076707Sbrutus 		ddi_put32(state->is_reg_handle,
2086707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR], estat);
2096707Sbrutus 	}
2106707Sbrutus 
2116707Sbrutus 	/* allocate and initialize the descriptor buf */
2126707Sbrutus 	e = ioat_ring_alloc(channel, size);
2136707Sbrutus 	if (e != DDI_SUCCESS) {
2146707Sbrutus 		goto chinitfail_desc_alloc;
2156707Sbrutus 	}
2166707Sbrutus 
2176707Sbrutus 	/* allocate and initialize the completion space */
2186707Sbrutus 	e = ioat_completion_alloc(channel);
2196707Sbrutus 	if (e != DDI_SUCCESS) {
2206707Sbrutus 		goto chinitfail_completion_alloc;
2216707Sbrutus 	}
2226707Sbrutus 
2236707Sbrutus 	/* setup kmem_cache for commands */
2246707Sbrutus 	cmd_size = sizeof (struct dcopy_cmd_s) +
2256707Sbrutus 	    sizeof (struct dcopy_cmd_priv_s) +
2266707Sbrutus 	    sizeof (struct ioat_cmd_private_s);
2276707Sbrutus 	(void) snprintf(chanstr, CHANSTRSIZE, "ioat%dchan%dcmd",
2286707Sbrutus 	    state->is_instance, channel->ic_chan_num);
2296707Sbrutus 	channel->ic_cmd_cache = kmem_cache_create(chanstr, cmd_size, 64,
2306707Sbrutus 	    NULL, NULL, NULL, NULL, NULL, 0);
2316707Sbrutus 	if (channel->ic_cmd_cache == NULL) {
2326707Sbrutus 		goto chinitfail_kmem_cache;
2336707Sbrutus 	}
2346707Sbrutus 
2356707Sbrutus 	/* start-up the channel */
2366707Sbrutus 	ioat_channel_start(channel);
2376707Sbrutus 
2386707Sbrutus 	/* fill in the channel info returned to dcopy */
2396707Sbrutus 	info->qc_version = DCOPY_QUERY_CHANNEL_V0;
2406707Sbrutus 	info->qc_id = state->is_deviceinfo.di_id;
2416707Sbrutus 	info->qc_capabilities = (uint64_t)state->is_capabilities;
2426707Sbrutus 	info->qc_channel_size = (uint64_t)size;
2436707Sbrutus 	info->qc_chan_num = (uint64_t)channel->ic_chan_num;
2446707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
2456707Sbrutus 		info->qc_dca_supported = B_FALSE;
2466707Sbrutus 	} else {
2476707Sbrutus 		if (info->qc_capabilities & IOAT_DMACAP_DCA) {
2486707Sbrutus 			info->qc_dca_supported = B_TRUE;
2496707Sbrutus 		} else {
2506707Sbrutus 			info->qc_dca_supported = B_FALSE;
2516707Sbrutus 		}
2526707Sbrutus 	}
2536707Sbrutus 
2546707Sbrutus 	*chan = channel;
2556707Sbrutus 
2566707Sbrutus 	return (DCOPY_SUCCESS);
2576707Sbrutus 
2586707Sbrutus chinitfail_kmem_cache:
2596707Sbrutus 	ioat_completion_free(channel);
2606707Sbrutus chinitfail_completion_alloc:
2616707Sbrutus 	ioat_ring_free(channel);
2626707Sbrutus chinitfail_desc_alloc:
2636707Sbrutus 	return (DCOPY_FAILURE);
2646707Sbrutus }
2656707Sbrutus 
2666707Sbrutus 
2676707Sbrutus /*
2686707Sbrutus  * ioat_channel_suspend()
2696707Sbrutus  */
2706707Sbrutus /*ARGSUSED*/
2716707Sbrutus void
2726707Sbrutus ioat_channel_suspend(ioat_state_t *state)
2736707Sbrutus {
2746707Sbrutus 	/*
2756707Sbrutus 	 * normally you would disable interrupts and reset the H/W here. But
2766707Sbrutus 	 * since the suspend framework doesn't know who is using us, it may
2776707Sbrutus 	 * not suspend their I/O before us.  Since we won't actively be doing
2786707Sbrutus 	 * any DMA or interrupts unless someone asks us to, it's safe to not
2796707Sbrutus 	 * do anything here.
2806707Sbrutus 	 */
2816707Sbrutus }
2826707Sbrutus 
2836707Sbrutus 
2846707Sbrutus /*
2856707Sbrutus  * ioat_channel_resume()
2866707Sbrutus  */
2876707Sbrutus int
2886707Sbrutus ioat_channel_resume(ioat_state_t *state)
2896707Sbrutus {
2906707Sbrutus 	ioat_channel_ring_t *ring;
2916707Sbrutus 	ioat_channel_t channel;
2926707Sbrutus 	uint32_t estat;
2936707Sbrutus 	int i;
2946707Sbrutus 
2956707Sbrutus 
2966707Sbrutus 	for (i = 0; i < state->is_num_channels; i++) {
2976707Sbrutus 		channel = &state->is_channel[i];
2986707Sbrutus 		ring = channel->ic_ring;
2996707Sbrutus 
3006707Sbrutus 		if (!channel->ic_inuse) {
3016707Sbrutus 			continue;
3026707Sbrutus 		}
3036707Sbrutus 
3046707Sbrutus 		/*
3056707Sbrutus 		 * Configure DMA channel
3066707Sbrutus 		 *   Channel In Use
3076707Sbrutus 		 *   Error Interrupt Enable
3086707Sbrutus 		 *   Any Error Abort Enable
3096707Sbrutus 		 *   Error Completion Enable
3106707Sbrutus 		 */
3116707Sbrutus 		ddi_put16(state->is_reg_handle,
3126707Sbrutus 		    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x011C);
3136707Sbrutus 
3146707Sbrutus 		/* check channel error register, clear any errors */
3156707Sbrutus 		estat = ddi_get32(state->is_reg_handle,
3166707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
3176707Sbrutus 		if (estat != 0) {
3186707Sbrutus #ifdef	DEBUG
3196707Sbrutus 			cmn_err(CE_CONT, "cleared errors (0x%x) before channel"
3206707Sbrutus 			    " (%d) enable\n", estat, channel->ic_chan_num);
3216707Sbrutus #endif
3226707Sbrutus 			ddi_put32(state->is_reg_handle,
3236707Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR],
3246707Sbrutus 			    estat);
3256707Sbrutus 		}
3266707Sbrutus 
3276707Sbrutus 		/* Re-initialize the ring */
3286707Sbrutus 		bzero(ring->cr_desc, channel->ic_desc_alloc_size);
3296707Sbrutus 		/* write the physical address into the chain address register */
3306707Sbrutus 		if (channel->ic_ver == IOAT_CBv1) {
3316707Sbrutus 			ddi_put32(state->is_reg_handle,
3326707Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO],
3336707Sbrutus 			    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
3346707Sbrutus 			ddi_put32(state->is_reg_handle,
3356707Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI],
3366707Sbrutus 			    (uint32_t)(ring->cr_phys_desc >> 32));
3376707Sbrutus 		} else {
3386707Sbrutus 			ASSERT(channel->ic_ver == IOAT_CBv2);
3396707Sbrutus 			ddi_put32(state->is_reg_handle,
3406707Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO],
3416707Sbrutus 			    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
3426707Sbrutus 			ddi_put32(state->is_reg_handle,
3436707Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI],
3446707Sbrutus 			    (uint32_t)(ring->cr_phys_desc >> 32));
3456707Sbrutus 		}
3466707Sbrutus 
3476707Sbrutus 		/* re-initialize the completion buffer */
3486707Sbrutus 		bzero((void *)channel->ic_cmpl, channel->ic_cmpl_alloc_size);
3496707Sbrutus 		/* write the phys addr into the completion address register */
3506707Sbrutus 		ddi_put32(state->is_reg_handle,
3516707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO],
3526707Sbrutus 		    (uint32_t)(channel->ic_phys_cmpl & 0xffffffff));
3536707Sbrutus 		ddi_put32(state->is_reg_handle,
3546707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI],
3556707Sbrutus 		    (uint32_t)(channel->ic_phys_cmpl >> 32));
3566707Sbrutus 
3576707Sbrutus 		/* start-up the channel */
3586707Sbrutus 		ioat_channel_start(channel);
3596707Sbrutus 
3606707Sbrutus 	}
3616707Sbrutus 
3626707Sbrutus 	return (DDI_SUCCESS);
3636707Sbrutus }
3646707Sbrutus 
3657656SSherry.Moore@Sun.COM /*
3667656SSherry.Moore@Sun.COM  * quiesce(9E) entry point.
3677656SSherry.Moore@Sun.COM  *
3687656SSherry.Moore@Sun.COM  * This function is called when the system is single-threaded at high
3697656SSherry.Moore@Sun.COM  * PIL with preemption disabled. Therefore, this function must not be
3707656SSherry.Moore@Sun.COM  * blocked.
3717656SSherry.Moore@Sun.COM  *
3727656SSherry.Moore@Sun.COM  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
3737656SSherry.Moore@Sun.COM  * DDI_FAILURE indicates an error condition and should almost never happen.
3747656SSherry.Moore@Sun.COM  */
3757656SSherry.Moore@Sun.COM void
3767656SSherry.Moore@Sun.COM ioat_channel_quiesce(ioat_state_t *state)
3777656SSherry.Moore@Sun.COM {
3787656SSherry.Moore@Sun.COM 	int i;
3797656SSherry.Moore@Sun.COM 
3807656SSherry.Moore@Sun.COM 	/*
3817656SSherry.Moore@Sun.COM 	 * Walk through all channels and quiesce
3827656SSherry.Moore@Sun.COM 	 */
3837656SSherry.Moore@Sun.COM 	for (i = 0; i < state->is_num_channels; i++) {
3847656SSherry.Moore@Sun.COM 
3857656SSherry.Moore@Sun.COM 		ioat_channel_t	channel = state->is_channel + i;
3867656SSherry.Moore@Sun.COM 
3877656SSherry.Moore@Sun.COM 		if (!channel->ic_inuse)
3887656SSherry.Moore@Sun.COM 			continue;
3897656SSherry.Moore@Sun.COM 
3907656SSherry.Moore@Sun.COM 		/* disable the interrupts */
3917656SSherry.Moore@Sun.COM 		ddi_put16(state->is_reg_handle,
3927656SSherry.Moore@Sun.COM 		    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL],
3937656SSherry.Moore@Sun.COM 		    0x0);
3947656SSherry.Moore@Sun.COM 
3957656SSherry.Moore@Sun.COM 		ioat_channel_reset(channel);
3967656SSherry.Moore@Sun.COM 	}
3977656SSherry.Moore@Sun.COM }
3987656SSherry.Moore@Sun.COM 
3996707Sbrutus 
4006707Sbrutus /*
4016707Sbrutus  * ioat_channel_free()
4026707Sbrutus  */
4036707Sbrutus void
4046707Sbrutus ioat_channel_free(void *channel_private)
4056707Sbrutus {
4066707Sbrutus 	struct ioat_channel_s *channel;
4076707Sbrutus 	ioat_channel_t *chan;
4086707Sbrutus 	ioat_state_t *state;
4096707Sbrutus 	uint_t chan_num;
4106707Sbrutus 
4116707Sbrutus 
4126707Sbrutus 	chan = (ioat_channel_t *)channel_private;
4136707Sbrutus 	channel = *chan;
4146707Sbrutus 
4156707Sbrutus 	state = channel->ic_state;
4166707Sbrutus 	chan_num = channel->ic_chan_num;
4176707Sbrutus 
4186707Sbrutus 	/* disable the interrupts */
4196707Sbrutus 	ddi_put16(state->is_reg_handle,
4206707Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x0);
4216707Sbrutus 
4226707Sbrutus 	ioat_channel_reset(channel);
4236707Sbrutus 
4246707Sbrutus 	/* cleanup command cache */
4256707Sbrutus 	kmem_cache_destroy(channel->ic_cmd_cache);
4266707Sbrutus 
4276707Sbrutus 	/* clean-up/free-up the completion space and descriptors */
4286707Sbrutus 	ioat_completion_free(channel);
4296707Sbrutus 	ioat_ring_free(channel);
4306707Sbrutus 
4316707Sbrutus 	channel->ic_inuse = B_FALSE;
4326707Sbrutus 
4336707Sbrutus 	/* free the H/W DMA engine */
4346707Sbrutus 	ioat_rs_free(state->is_channel_rs, chan_num);
4356707Sbrutus 
4366707Sbrutus 	*chan = NULL;
4376707Sbrutus }
4386707Sbrutus 
4396707Sbrutus 
4406707Sbrutus /*
4416707Sbrutus  * ioat_channel_intr()
4426707Sbrutus  */
4436707Sbrutus void
4446707Sbrutus ioat_channel_intr(ioat_channel_t channel)
4456707Sbrutus {
4466707Sbrutus 	ioat_state_t *state;
4476707Sbrutus 	uint16_t chanctrl;
4486707Sbrutus 	uint32_t chanerr;
4496707Sbrutus 	uint32_t status;
4506707Sbrutus 
4516707Sbrutus 
4526707Sbrutus 	state = channel->ic_state;
4536707Sbrutus 
4546707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
4556707Sbrutus 		status = ddi_get32(state->is_reg_handle,
4566707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_STS_LO]);
4576707Sbrutus 	} else {
4586707Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
4596707Sbrutus 		status = ddi_get32(state->is_reg_handle,
4606707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_STS_LO]);
4616707Sbrutus 	}
4626707Sbrutus 
4636707Sbrutus 	/* if that status isn't ACTIVE or IDLE, the channel has failed */
4646707Sbrutus 	if (status & IOAT_CHAN_STS_FAIL_MASK) {
4656707Sbrutus 		chanerr = ddi_get32(state->is_reg_handle,
4666707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
4676707Sbrutus 		cmn_err(CE_WARN, "channel(%d) fatal failure! "
4686707Sbrutus 		    "chanstat_lo=0x%X; chanerr=0x%X\n",
4696707Sbrutus 		    channel->ic_chan_num, status, chanerr);
4706707Sbrutus 		channel->ic_channel_state = IOAT_CHANNEL_IN_FAILURE;
4716707Sbrutus 		ioat_channel_reset(channel);
4726707Sbrutus 
4736707Sbrutus 		return;
4746707Sbrutus 	}
4756707Sbrutus 
4766707Sbrutus 	/*
4776707Sbrutus 	 * clear interrupt disable bit if set (it's a RW1C). Read it back to
4786707Sbrutus 	 * ensure the write completes.
4796707Sbrutus 	 */
4806707Sbrutus 	chanctrl = ddi_get16(state->is_reg_handle,
4816707Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL]);
4826707Sbrutus 	ddi_put16(state->is_reg_handle,
4836707Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], chanctrl);
4846707Sbrutus 	(void) ddi_get16(state->is_reg_handle,
4856707Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL]);
4866707Sbrutus 
4876707Sbrutus 	/* tell dcopy we have seen a completion on this channel */
4886707Sbrutus 	dcopy_device_channel_notify(channel->ic_dcopy_handle, DCOPY_COMPLETION);
4896707Sbrutus }
4906707Sbrutus 
4916707Sbrutus 
4926707Sbrutus /*
4936707Sbrutus  * ioat_channel_start()
4946707Sbrutus  */
4956707Sbrutus void
4966707Sbrutus ioat_channel_start(ioat_channel_t channel)
4976707Sbrutus {
4986707Sbrutus 	ioat_chan_dma_desc_t desc;
4996707Sbrutus 
5006707Sbrutus 	/* set the first descriptor up as a NULL descriptor */
5016707Sbrutus 	bzero(&desc, sizeof (desc));
5026707Sbrutus 	desc.dd_size = 0;
5036707Sbrutus 	desc.dd_ctrl = IOAT_DESC_CTRL_OP_DMA | IOAT_DESC_DMACTRL_NULL |
5046707Sbrutus 	    IOAT_DESC_CTRL_CMPL;
5056707Sbrutus 	desc.dd_next_desc = 0x0;
5066707Sbrutus 
5076707Sbrutus 	/* setup the very first descriptor */
5086707Sbrutus 	ioat_ring_seed(channel, &desc);
5096707Sbrutus }
5106707Sbrutus 
5116707Sbrutus 
5126707Sbrutus /*
5136707Sbrutus  * ioat_channel_reset()
5146707Sbrutus  */
5156707Sbrutus void
5166707Sbrutus ioat_channel_reset(ioat_channel_t channel)
5176707Sbrutus {
5186707Sbrutus 	ioat_state_t *state;
5196707Sbrutus 
5206707Sbrutus 	state = channel->ic_state;
5216707Sbrutus 
5226707Sbrutus 	/* hit the reset bit */
5236707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
5246707Sbrutus 		ddi_put8(state->is_reg_handle,
5256707Sbrutus 		    &channel->ic_regs[IOAT_V1_CHAN_CMD], 0x20);
5266707Sbrutus 	} else {
5276707Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
5286707Sbrutus 		ddi_put8(state->is_reg_handle,
5296707Sbrutus 		    &channel->ic_regs[IOAT_V2_CHAN_CMD], 0x20);
5306707Sbrutus 	}
5316707Sbrutus }
5326707Sbrutus 
5336707Sbrutus 
5346707Sbrutus /*
5356707Sbrutus  * ioat_completion_alloc()
5366707Sbrutus  */
5376707Sbrutus int
5386707Sbrutus ioat_completion_alloc(ioat_channel_t channel)
5396707Sbrutus {
5406707Sbrutus 	ioat_state_t *state;
5416707Sbrutus 	size_t real_length;
5426707Sbrutus 	uint_t cookie_cnt;
5436707Sbrutus 	int e;
5446707Sbrutus 
5456707Sbrutus 
5466707Sbrutus 	state = channel->ic_state;
5476707Sbrutus 
5486707Sbrutus 	/*
5496707Sbrutus 	 * allocate memory for the completion status, zero it out, and get
5506707Sbrutus 	 * the paddr. We'll allocate a physically contiguous cache line.
5516707Sbrutus 	 */
5526707Sbrutus 	e = ddi_dma_alloc_handle(state->is_dip, &ioat_cmpl_dma_attr,
5536707Sbrutus 	    DDI_DMA_SLEEP, NULL, &channel->ic_cmpl_dma_handle);
5546707Sbrutus 	if (e != DDI_SUCCESS) {
5556707Sbrutus 		goto cmplallocfail_alloc_handle;
5566707Sbrutus 	}
5576707Sbrutus 	channel->ic_cmpl_alloc_size = 64;
5586707Sbrutus 	e = ddi_dma_mem_alloc(channel->ic_cmpl_dma_handle,
5596707Sbrutus 	    channel->ic_cmpl_alloc_size, &ioat_acc_attr,
5606707Sbrutus 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
5616707Sbrutus 	    (caddr_t *)&channel->ic_cmpl, &real_length,
5626707Sbrutus 	    &channel->ic_cmpl_handle);
5636707Sbrutus 	if (e != DDI_SUCCESS) {
5646707Sbrutus 		goto cmplallocfail_mem_alloc;
5656707Sbrutus 	}
5666707Sbrutus 	bzero((void *)channel->ic_cmpl, channel->ic_cmpl_alloc_size);
5676707Sbrutus 	e = ddi_dma_addr_bind_handle(channel->ic_cmpl_dma_handle, NULL,
5686707Sbrutus 	    (caddr_t)channel->ic_cmpl, channel->ic_cmpl_alloc_size,
5696707Sbrutus 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
5706707Sbrutus 	    &channel->ic_cmpl_cookie, &cookie_cnt);
5716707Sbrutus 	if (e != DDI_SUCCESS) {
5726707Sbrutus 		goto cmplallocfail_addr_bind;
5736707Sbrutus 	}
5746707Sbrutus 	ASSERT(cookie_cnt == 1);
5756707Sbrutus 	ASSERT(channel->ic_cmpl_cookie.dmac_size ==
5766707Sbrutus 	    channel->ic_cmpl_alloc_size);
5776707Sbrutus 	channel->ic_phys_cmpl = channel->ic_cmpl_cookie.dmac_laddress;
5786707Sbrutus 
5796707Sbrutus 	/* write the physical address into the completion address register */
5806707Sbrutus 	ddi_put32(state->is_reg_handle,
5816707Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO],
5826707Sbrutus 	    (uint32_t)(channel->ic_phys_cmpl & 0xffffffff));
5836707Sbrutus 	ddi_put32(state->is_reg_handle,
5846707Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI],
5856707Sbrutus 	    (uint32_t)(channel->ic_phys_cmpl >> 32));
5866707Sbrutus 
5876707Sbrutus 	return (DDI_SUCCESS);
5886707Sbrutus 
5896707Sbrutus cmplallocfail_addr_bind:
5906707Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
5916707Sbrutus cmplallocfail_mem_alloc:
5926707Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
5936707Sbrutus cmplallocfail_alloc_handle:
5946707Sbrutus 	return (DDI_FAILURE);
5956707Sbrutus }
5966707Sbrutus 
5976707Sbrutus 
5986707Sbrutus /*
5996707Sbrutus  * ioat_completion_free()
6006707Sbrutus  */
6016707Sbrutus void
6026707Sbrutus ioat_completion_free(ioat_channel_t channel)
6036707Sbrutus {
6046707Sbrutus 	ioat_state_t *state;
6056707Sbrutus 
6066707Sbrutus 	state = channel->ic_state;
6076707Sbrutus 
6086707Sbrutus 	/* reset the completion address register */
6096707Sbrutus 	ddi_put32(state->is_reg_handle,
6106707Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO], 0x0);
6116707Sbrutus 	ddi_put32(state->is_reg_handle,
6126707Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI], 0x0);
6136707Sbrutus 
6146707Sbrutus 	/* unbind, then free up the memory, dma handle */
6156707Sbrutus 	(void) ddi_dma_unbind_handle(channel->ic_cmpl_dma_handle);
6166707Sbrutus 	ddi_dma_mem_free(&channel->ic_cmpl_handle);
6176707Sbrutus 	ddi_dma_free_handle(&channel->ic_cmpl_dma_handle);
6186707Sbrutus }
6196707Sbrutus 
6206707Sbrutus /*
6216707Sbrutus  * ioat_ring_alloc()
6226707Sbrutus  */
6236707Sbrutus int
6246707Sbrutus ioat_ring_alloc(ioat_channel_t channel, uint_t desc_cnt)
6256707Sbrutus {
6266707Sbrutus 	ioat_channel_ring_t *ring;
6276707Sbrutus 	ioat_state_t *state;
6286707Sbrutus 	size_t real_length;
6296707Sbrutus 	uint_t cookie_cnt;
6306707Sbrutus 	int e;
6316707Sbrutus 
6326707Sbrutus 
6336707Sbrutus 	state = channel->ic_state;
6346707Sbrutus 
6356707Sbrutus 	ring = kmem_zalloc(sizeof (ioat_channel_ring_t), KM_SLEEP);
6366707Sbrutus 	channel->ic_ring = ring;
6376707Sbrutus 	ring->cr_chan = channel;
6386707Sbrutus 	ring->cr_post_cnt = 0;
6396707Sbrutus 
6406707Sbrutus 	mutex_init(&ring->cr_cmpl_mutex, NULL, MUTEX_DRIVER,
6416707Sbrutus 	    channel->ic_state->is_iblock_cookie);
6426707Sbrutus 	mutex_init(&ring->cr_desc_mutex, NULL, MUTEX_DRIVER,
6436707Sbrutus 	    channel->ic_state->is_iblock_cookie);
6446707Sbrutus 
6456707Sbrutus 	/*
6466707Sbrutus 	 * allocate memory for the ring, zero it out, and get the paddr.
6476707Sbrutus 	 * We'll allocate a physically contiguous chunck of memory  which
6486707Sbrutus 	 * simplifies the completion logic.
6496707Sbrutus 	 */
6506707Sbrutus 	e = ddi_dma_alloc_handle(state->is_dip, &ioat_desc_dma_attr,
6516707Sbrutus 	    DDI_DMA_SLEEP, NULL, &channel->ic_desc_dma_handle);
6526707Sbrutus 	if (e != DDI_SUCCESS) {
6536707Sbrutus 		goto ringallocfail_alloc_handle;
6546707Sbrutus 	}
6556707Sbrutus 	/*
6566707Sbrutus 	 * allocate one extra descriptor so we can simplify the empty/full
6576707Sbrutus 	 * logic. Then round that number up to a whole multiple of 4.
6586707Sbrutus 	 */
6596707Sbrutus 	channel->ic_chan_desc_cnt = ((desc_cnt + 1) + 3) & ~0x3;
6606707Sbrutus 	ring->cr_desc_last = channel->ic_chan_desc_cnt - 1;
6616707Sbrutus 	channel->ic_desc_alloc_size = channel->ic_chan_desc_cnt *
6626707Sbrutus 	    sizeof (ioat_chan_desc_t);
6636707Sbrutus 	e = ddi_dma_mem_alloc(channel->ic_desc_dma_handle,
6646707Sbrutus 	    channel->ic_desc_alloc_size, &ioat_acc_attr,
6656707Sbrutus 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
6666707Sbrutus 	    (caddr_t *)&ring->cr_desc, &real_length, &channel->ic_desc_handle);
6676707Sbrutus 	if (e != DDI_SUCCESS) {
6686707Sbrutus 		goto ringallocfail_mem_alloc;
6696707Sbrutus 	}
6706707Sbrutus 	bzero(ring->cr_desc, channel->ic_desc_alloc_size);
6716707Sbrutus 	e = ddi_dma_addr_bind_handle(channel->ic_desc_dma_handle, NULL,
6726707Sbrutus 	    (caddr_t)ring->cr_desc, channel->ic_desc_alloc_size,
6736707Sbrutus 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
6746707Sbrutus 	    &channel->ic_desc_cookies, &cookie_cnt);
6756707Sbrutus 	if (e != DDI_SUCCESS) {
6766707Sbrutus 		goto ringallocfail_addr_bind;
6776707Sbrutus 	}
6786707Sbrutus 	ASSERT(cookie_cnt == 1);
6796707Sbrutus 	ASSERT(channel->ic_desc_cookies.dmac_size ==
6806707Sbrutus 	    channel->ic_desc_alloc_size);
6816707Sbrutus 	ring->cr_phys_desc = channel->ic_desc_cookies.dmac_laddress;
6826707Sbrutus 
6836707Sbrutus 	/* write the physical address into the chain address register */
6846707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
6856707Sbrutus 		ddi_put32(state->is_reg_handle,
6866707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO],
6876707Sbrutus 		    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
6886707Sbrutus 		ddi_put32(state->is_reg_handle,
6896707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI],
6906707Sbrutus 		    (uint32_t)(ring->cr_phys_desc >> 32));
6916707Sbrutus 	} else {
6926707Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
6936707Sbrutus 		ddi_put32(state->is_reg_handle,
6946707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO],
6956707Sbrutus 		    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
6966707Sbrutus 		ddi_put32(state->is_reg_handle,
6976707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI],
6986707Sbrutus 		    (uint32_t)(ring->cr_phys_desc >> 32));
6996707Sbrutus 	}
7006707Sbrutus 
7016707Sbrutus 	return (DCOPY_SUCCESS);
7026707Sbrutus 
7036707Sbrutus ringallocfail_addr_bind:
7046707Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
7056707Sbrutus ringallocfail_mem_alloc:
7066707Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
7076707Sbrutus ringallocfail_alloc_handle:
7086707Sbrutus 	mutex_destroy(&ring->cr_desc_mutex);
7096707Sbrutus 	mutex_destroy(&ring->cr_cmpl_mutex);
7106707Sbrutus 	kmem_free(channel->ic_ring, sizeof (ioat_channel_ring_t));
7116707Sbrutus 
7126707Sbrutus 	return (DCOPY_FAILURE);
7136707Sbrutus }
7146707Sbrutus 
7156707Sbrutus 
7166707Sbrutus /*
7176707Sbrutus  * ioat_ring_free()
7186707Sbrutus  */
7196707Sbrutus void
7206707Sbrutus ioat_ring_free(ioat_channel_t channel)
7216707Sbrutus {
7226707Sbrutus 	ioat_state_t *state;
7236707Sbrutus 
7246707Sbrutus 
7256707Sbrutus 	state = channel->ic_state;
7266707Sbrutus 
7276707Sbrutus 	/* reset the chain address register */
7286707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
7296707Sbrutus 		ddi_put32(state->is_reg_handle,
7306707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO], 0x0);
7316707Sbrutus 		ddi_put32(state->is_reg_handle,
7326707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI], 0x0);
7336707Sbrutus 	} else {
7346707Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
7356707Sbrutus 		ddi_put32(state->is_reg_handle,
7366707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO], 0x0);
7376707Sbrutus 		ddi_put32(state->is_reg_handle,
7386707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI], 0x0);
7396707Sbrutus 	}
7406707Sbrutus 
7416707Sbrutus 	/* unbind, then free up the memory, dma handle */
7426707Sbrutus 	(void) ddi_dma_unbind_handle(channel->ic_desc_dma_handle);
7436707Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
7446707Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
7456707Sbrutus 
7466707Sbrutus 	mutex_destroy(&channel->ic_ring->cr_desc_mutex);
7476707Sbrutus 	mutex_destroy(&channel->ic_ring->cr_cmpl_mutex);
7486707Sbrutus 	kmem_free(channel->ic_ring, sizeof (ioat_channel_ring_t));
7496707Sbrutus 
7506707Sbrutus }
7516707Sbrutus 
7526707Sbrutus 
7536707Sbrutus /*
7546707Sbrutus  * ioat_ring_seed()
7556707Sbrutus  *    write the first descriptor in the ring.
7566707Sbrutus  */
7576707Sbrutus void
7586707Sbrutus ioat_ring_seed(ioat_channel_t channel, ioat_chan_dma_desc_t *in_desc)
7596707Sbrutus {
7606707Sbrutus 	ioat_channel_ring_t *ring;
7616707Sbrutus 	ioat_chan_dma_desc_t *desc;
7626707Sbrutus 	ioat_chan_dma_desc_t *prev;
7636707Sbrutus 	ioat_state_t *state;
7646707Sbrutus 
7656707Sbrutus 
7666707Sbrutus 	state = channel->ic_state;
7676707Sbrutus 	ring = channel->ic_ring;
7686707Sbrutus 
7696707Sbrutus 	/* init the completion state */
7706707Sbrutus 	ring->cr_cmpl_gen = 0x0;
7716707Sbrutus 	ring->cr_cmpl_last = 0x0;
7726707Sbrutus 
7736707Sbrutus 	/* write in the descriptor and init the descriptor state */
7746707Sbrutus 	ring->cr_post_cnt++;
7756707Sbrutus 	channel->ic_ring->cr_desc[0] = *(ioat_chan_desc_t *)in_desc;
7766707Sbrutus 	ring->cr_desc_gen = 0;
7776707Sbrutus 	ring->cr_desc_prev = 0;
7786707Sbrutus 	ring->cr_desc_next = 1;
7796707Sbrutus 
7806707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
7816707Sbrutus 		/* hit the start bit */
7826707Sbrutus 		ddi_put8(state->is_reg_handle,
7836707Sbrutus 		    &channel->ic_regs[IOAT_V1_CHAN_CMD], 0x1);
7846707Sbrutus 	} else {
7856707Sbrutus 		/*
7866707Sbrutus 		 * if this is CBv2, link the descriptor to an empty
7876707Sbrutus 		 * descriptor
7886707Sbrutus 		 */
7896707Sbrutus 		ASSERT(ring->cr_chan->ic_ver == IOAT_CBv2);
7906707Sbrutus 		desc = (ioat_chan_dma_desc_t *)
7916707Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
7926707Sbrutus 		prev = (ioat_chan_dma_desc_t *)
7936707Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
7946707Sbrutus 
7956707Sbrutus 		desc->dd_ctrl = 0;
7966707Sbrutus 		desc->dd_next_desc = 0x0;
7976707Sbrutus 
7986707Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
7996707Sbrutus 		    (ring->cr_desc_next << 6);
8006707Sbrutus 
8016707Sbrutus 		ddi_put16(state->is_reg_handle,
8026707Sbrutus 		    (uint16_t *)&channel->ic_regs[IOAT_V2_CHAN_CNT],
8036707Sbrutus 		    (uint16_t)1);
8046707Sbrutus 	}
8056707Sbrutus 
8066707Sbrutus }
8076707Sbrutus 
8086707Sbrutus 
8096707Sbrutus /*
8106707Sbrutus  * ioat_cmd_alloc()
8116707Sbrutus  */
8126707Sbrutus int
8136707Sbrutus ioat_cmd_alloc(void *private, int flags, dcopy_cmd_t *cmd)
8146707Sbrutus {
8156707Sbrutus 	ioat_cmd_private_t *priv;
8166707Sbrutus 	ioat_channel_t channel;
8176707Sbrutus 	dcopy_cmd_t oldcmd;
8186707Sbrutus 	int kmflag;
8196707Sbrutus 
8206707Sbrutus 
8216707Sbrutus 	channel = (ioat_channel_t)private;
8226707Sbrutus 
8236707Sbrutus 	if (flags & DCOPY_NOSLEEP) {
8246707Sbrutus 		kmflag = KM_NOSLEEP;
8256707Sbrutus 	} else {
8266707Sbrutus 		kmflag = KM_SLEEP;
8276707Sbrutus 	}
8286707Sbrutus 
8296707Sbrutus 	/* save the command passed incase DCOPY_ALLOC_LINK is set */
8306707Sbrutus 	oldcmd = *cmd;
8316707Sbrutus 
8326707Sbrutus 	*cmd = kmem_cache_alloc(channel->ic_cmd_cache, kmflag);
8336707Sbrutus 	if (*cmd == NULL) {
8346707Sbrutus 		return (DCOPY_NORESOURCES);
8356707Sbrutus 	}
8366707Sbrutus 
8376707Sbrutus 	/* setup the dcopy and ioat private state pointers */
8386707Sbrutus 	(*cmd)->dp_version = DCOPY_CMD_V0;
8396707Sbrutus 	(*cmd)->dp_cmd = 0;
8406707Sbrutus 	(*cmd)->dp_private = (struct dcopy_cmd_priv_s *)
8416707Sbrutus 	    ((uintptr_t)(*cmd) + sizeof (struct dcopy_cmd_s));
8426707Sbrutus 	(*cmd)->dp_private->pr_device_cmd_private =
8436707Sbrutus 	    (struct ioat_cmd_private_s *)((uintptr_t)(*cmd)->dp_private +
8446707Sbrutus 	    sizeof (struct dcopy_cmd_priv_s));
8456707Sbrutus 
8466707Sbrutus 	/*
8476707Sbrutus 	 * if DCOPY_ALLOC_LINK is set, link the old command to the new one
8486707Sbrutus 	 * just allocated.
8496707Sbrutus 	 */
8506707Sbrutus 	priv = (*cmd)->dp_private->pr_device_cmd_private;
8516707Sbrutus 	if (flags & DCOPY_ALLOC_LINK) {
8526707Sbrutus 		priv->ip_next = oldcmd;
8536707Sbrutus 	} else {
8546707Sbrutus 		priv->ip_next = NULL;
8556707Sbrutus 	}
8566707Sbrutus 
8576707Sbrutus 	return (DCOPY_SUCCESS);
8586707Sbrutus }
8596707Sbrutus 
8606707Sbrutus 
8616707Sbrutus /*
8626707Sbrutus  * ioat_cmd_free()
8636707Sbrutus  */
8646707Sbrutus void
8656707Sbrutus ioat_cmd_free(void *private, dcopy_cmd_t *cmdp)
8666707Sbrutus {
8676707Sbrutus 	ioat_cmd_private_t *priv;
8686707Sbrutus 	ioat_channel_t channel;
8696707Sbrutus 	dcopy_cmd_t next;
8706707Sbrutus 	dcopy_cmd_t cmd;
8716707Sbrutus 
8726707Sbrutus 
8736707Sbrutus 	channel = (ioat_channel_t)private;
8746707Sbrutus 	cmd = *(cmdp);
8756707Sbrutus 
8766707Sbrutus 	/*
8776707Sbrutus 	 * free all the commands in the chain (see DCOPY_ALLOC_LINK in
8786707Sbrutus 	 * ioat_cmd_alloc() for more info).
8796707Sbrutus 	 */
8806707Sbrutus 	while (cmd != NULL) {
8816707Sbrutus 		priv = cmd->dp_private->pr_device_cmd_private;
8826707Sbrutus 		next = priv->ip_next;
8836707Sbrutus 		kmem_cache_free(channel->ic_cmd_cache, cmd);
8846707Sbrutus 		cmd = next;
8856707Sbrutus 	}
8866707Sbrutus 	*cmdp = NULL;
8876707Sbrutus }
8886707Sbrutus 
8896707Sbrutus 
8906707Sbrutus /*
8916707Sbrutus  * ioat_cmd_post()
8926707Sbrutus  */
8936707Sbrutus int
8946707Sbrutus ioat_cmd_post(void *private, dcopy_cmd_t cmd)
8956707Sbrutus {
8966707Sbrutus 	ioat_channel_ring_t *ring;
8976707Sbrutus 	ioat_cmd_private_t *priv;
8986707Sbrutus 	ioat_channel_t channel;
8996707Sbrutus 	ioat_state_t *state;
9006707Sbrutus 	uint64_t dest_paddr;
9016707Sbrutus 	uint64_t src_paddr;
9026707Sbrutus 	uint64_t dest_addr;
9036707Sbrutus 	uint32_t dest_size;
9046707Sbrutus 	uint64_t src_addr;
9056707Sbrutus 	uint32_t src_size;
9066707Sbrutus 	size_t xfer_size;
9076707Sbrutus 	uint32_t ctrl;
9086707Sbrutus 	size_t size;
9096707Sbrutus 	int e;
9106707Sbrutus 
9116707Sbrutus 
9126707Sbrutus 	channel = (ioat_channel_t)private;
9136707Sbrutus 	priv = cmd->dp_private->pr_device_cmd_private;
9146707Sbrutus 
9156707Sbrutus 	state = channel->ic_state;
9166707Sbrutus 	ring = channel->ic_ring;
9176707Sbrutus 
9186707Sbrutus 	mutex_enter(&ring->cr_desc_mutex);
9196707Sbrutus 
9206707Sbrutus 	/* if the channel has had a fatal failure, return failure */
9216707Sbrutus 	if (channel->ic_channel_state == IOAT_CHANNEL_IN_FAILURE) {
9229064SMark.Johnson@Sun.COM 		mutex_exit(&ring->cr_desc_mutex);
9236707Sbrutus 		return (DCOPY_FAILURE);
9246707Sbrutus 	}
9256707Sbrutus 
9266707Sbrutus 	/* make sure we have space for the descriptors */
9276707Sbrutus 	e = ioat_ring_reserve(channel, ring, cmd);
9286707Sbrutus 	if (e != DCOPY_SUCCESS) {
9299064SMark.Johnson@Sun.COM 		mutex_exit(&ring->cr_desc_mutex);
9306707Sbrutus 		return (DCOPY_NORESOURCES);
9316707Sbrutus 	}
9326707Sbrutus 
9336707Sbrutus 	/* if we support DCA, and the DCA flag is set, post a DCA desc */
9346707Sbrutus 	if ((channel->ic_ver == IOAT_CBv2) &&
9356707Sbrutus 	    (cmd->dp_flags & DCOPY_CMD_DCA)) {
9366707Sbrutus 		ioat_cmd_post_dca(ring, cmd->dp_dca_id);
9376707Sbrutus 	}
9386707Sbrutus 
9396707Sbrutus 	/*
9406707Sbrutus 	 * the dma copy may have to be broken up into multiple descriptors
9416707Sbrutus 	 * since we can't cross a page boundary.
9426707Sbrutus 	 */
9436707Sbrutus 	ASSERT(cmd->dp_version == DCOPY_CMD_V0);
9446707Sbrutus 	ASSERT(cmd->dp_cmd == DCOPY_CMD_COPY);
9456707Sbrutus 	src_addr = cmd->dp.copy.cc_source;
9466707Sbrutus 	dest_addr = cmd->dp.copy.cc_dest;
9476707Sbrutus 	size = cmd->dp.copy.cc_size;
9486707Sbrutus 	while (size > 0) {
9496707Sbrutus 		src_paddr = pa_to_ma(src_addr);
9506707Sbrutus 		dest_paddr = pa_to_ma(dest_addr);
9516707Sbrutus 
9526707Sbrutus 		/* adjust for any offset into the page */
9536707Sbrutus 		if ((src_addr & PAGEOFFSET) == 0) {
9546707Sbrutus 			src_size = PAGESIZE;
9556707Sbrutus 		} else {
9566707Sbrutus 			src_size = PAGESIZE - (src_addr & PAGEOFFSET);
9576707Sbrutus 		}
9586707Sbrutus 		if ((dest_addr & PAGEOFFSET) == 0) {
9596707Sbrutus 			dest_size = PAGESIZE;
9606707Sbrutus 		} else {
9616707Sbrutus 			dest_size = PAGESIZE - (dest_addr & PAGEOFFSET);
9626707Sbrutus 		}
9636707Sbrutus 
9646707Sbrutus 		/* take the smallest of the three */
9656707Sbrutus 		xfer_size = MIN(src_size, dest_size);
9666707Sbrutus 		xfer_size = MIN(xfer_size, size);
9676707Sbrutus 
9686707Sbrutus 		/*
9696707Sbrutus 		 * if this is the last descriptor, and we are supposed to
9706707Sbrutus 		 * generate a completion, generate a completion. same logic
9716707Sbrutus 		 * for interrupt.
9726707Sbrutus 		 */
9736707Sbrutus 		ctrl = 0;
9746707Sbrutus 		if (xfer_size == size) {
9756707Sbrutus 			if (!(cmd->dp_flags & DCOPY_CMD_NOSTAT)) {
9766707Sbrutus 				ctrl |= IOAT_DESC_CTRL_CMPL;
9776707Sbrutus 			}
9786707Sbrutus 			if ((cmd->dp_flags & DCOPY_CMD_INTR)) {
9796707Sbrutus 				ctrl |= IOAT_DESC_CTRL_INTR;
9806707Sbrutus 			}
9816707Sbrutus 		}
9826707Sbrutus 
9836707Sbrutus 		ioat_cmd_post_copy(ring, src_paddr, dest_paddr, xfer_size,
9846707Sbrutus 		    ctrl);
9856707Sbrutus 
9866707Sbrutus 		/* go to the next page */
9876707Sbrutus 		src_addr += xfer_size;
9886707Sbrutus 		dest_addr += xfer_size;
9896707Sbrutus 		size -= xfer_size;
9906707Sbrutus 	}
9916707Sbrutus 
9926707Sbrutus 	/*
9936707Sbrutus 	 * if we are going to create a completion, save away the state so we
9946707Sbrutus 	 * can poll on it.
9956707Sbrutus 	 */
9966707Sbrutus 	if (!(cmd->dp_flags & DCOPY_CMD_NOSTAT)) {
9976707Sbrutus 		priv->ip_generation = ring->cr_desc_gen_prev;
9986707Sbrutus 		priv->ip_index = ring->cr_desc_prev;
9996707Sbrutus 	}
10006707Sbrutus 
10016707Sbrutus 	/* if queue not defined, tell the DMA engine about it */
10026707Sbrutus 	if (!(cmd->dp_flags & DCOPY_CMD_QUEUE)) {
10036707Sbrutus 		if (channel->ic_ver == IOAT_CBv1) {
10046707Sbrutus 			ddi_put8(state->is_reg_handle,
10056707Sbrutus 			    (uint8_t *)&channel->ic_regs[IOAT_V1_CHAN_CMD],
10066707Sbrutus 			    0x2);
10076707Sbrutus 		} else {
10086707Sbrutus 			ASSERT(channel->ic_ver == IOAT_CBv2);
10096707Sbrutus 			ddi_put16(state->is_reg_handle,
10106707Sbrutus 			    (uint16_t *)&channel->ic_regs[IOAT_V2_CHAN_CNT],
10116707Sbrutus 			    (uint16_t)(ring->cr_post_cnt & 0xFFFF));
10126707Sbrutus 		}
10136707Sbrutus 	}
10146707Sbrutus 
10156707Sbrutus 	mutex_exit(&ring->cr_desc_mutex);
10166707Sbrutus 
10176707Sbrutus 	return (DCOPY_SUCCESS);
10186707Sbrutus }
10196707Sbrutus 
10206707Sbrutus 
10216707Sbrutus /*
10226707Sbrutus  * ioat_cmd_post_dca()
10236707Sbrutus  */
10246707Sbrutus static void
10256707Sbrutus ioat_cmd_post_dca(ioat_channel_ring_t *ring, uint32_t dca_id)
10266707Sbrutus {
1027*9628SMark.Johnson@Sun.COM 	ioat_chan_dca_desc_t *saved_prev;
10286707Sbrutus 	ioat_chan_dca_desc_t *desc;
10296707Sbrutus 	ioat_chan_dca_desc_t *prev;
10306707Sbrutus 	ioat_channel_t channel;
1031*9628SMark.Johnson@Sun.COM 	uint64_t next_desc_phys;
1032*9628SMark.Johnson@Sun.COM 	off_t prev_offset;
1033*9628SMark.Johnson@Sun.COM 	off_t next_offset;
10346707Sbrutus 
10356707Sbrutus 
10366707Sbrutus 	channel = ring->cr_chan;
10376707Sbrutus 	desc = (ioat_chan_dca_desc_t *)&ring->cr_desc[ring->cr_desc_next];
10386707Sbrutus 	prev = (ioat_chan_dca_desc_t *)&ring->cr_desc[ring->cr_desc_prev];
10396707Sbrutus 
10406707Sbrutus 	/* keep track of the number of descs posted for cbv2 */
10416707Sbrutus 	ring->cr_post_cnt++;
10426707Sbrutus 
10436707Sbrutus 	/*
10446707Sbrutus 	 * post a context change desriptor. If dca has never been used on
10456707Sbrutus 	 * this channel, or if the id doesn't match the last id used on this
10466707Sbrutus 	 * channel, set CONTEXT_CHANGE bit and dca id, set dca state to active,
10476707Sbrutus 	 * and save away the id we're using.
10486707Sbrutus 	 */
10496707Sbrutus 	desc->dd_ctrl = IOAT_DESC_CTRL_OP_CNTX;
10506707Sbrutus 	desc->dd_next_desc = 0x0;
10516707Sbrutus 	if (!channel->ic_dca_active || (channel->ic_dca_current != dca_id)) {
10526707Sbrutus 		channel->ic_dca_active = B_TRUE;
10536707Sbrutus 		channel->ic_dca_current = dca_id;
10546707Sbrutus 		desc->dd_ctrl |= IOAT_DESC_CTRL_CNTX_CHNG;
10556707Sbrutus 		desc->dd_cntx = dca_id;
10566707Sbrutus 	}
10576707Sbrutus 
1058*9628SMark.Johnson@Sun.COM 	/*
1059*9628SMark.Johnson@Sun.COM 	 * save next desc and prev offset for when we link the two
1060*9628SMark.Johnson@Sun.COM 	 * descriptors together.
1061*9628SMark.Johnson@Sun.COM 	 */
1062*9628SMark.Johnson@Sun.COM 	saved_prev = prev;
1063*9628SMark.Johnson@Sun.COM 	prev_offset = ring->cr_desc_prev << 6;
1064*9628SMark.Johnson@Sun.COM 	next_offset = ring->cr_desc_next << 6;
1065*9628SMark.Johnson@Sun.COM 	next_desc_phys = ring->cr_phys_desc + next_offset;
10666707Sbrutus 
10676707Sbrutus 	/* save the current desc_next and desc_last for the completion */
10686707Sbrutus 	ring->cr_desc_prev = ring->cr_desc_next;
10696707Sbrutus 	ring->cr_desc_gen_prev = ring->cr_desc_gen;
10706707Sbrutus 
10716707Sbrutus 	/* increment next/gen so it points to the next free desc */
10726707Sbrutus 	ring->cr_desc_next++;
10736707Sbrutus 	if (ring->cr_desc_next > ring->cr_desc_last) {
10746707Sbrutus 		ring->cr_desc_next = 0;
10756707Sbrutus 		ring->cr_desc_gen++;
10766707Sbrutus 	}
10776707Sbrutus 
10786707Sbrutus 	/*
10796707Sbrutus 	 * if this is CBv2, link the descriptor to an empty descriptor. Since
10806707Sbrutus 	 * we always leave on desc empty to detect full, this works out.
10816707Sbrutus 	 */
10826707Sbrutus 	if (ring->cr_chan->ic_ver == IOAT_CBv2) {
10836707Sbrutus 		desc = (ioat_chan_dca_desc_t *)
10846707Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
10856707Sbrutus 		prev = (ioat_chan_dca_desc_t *)
10866707Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
10876707Sbrutus 		desc->dd_ctrl = 0;
10886707Sbrutus 		desc->dd_next_desc = 0x0;
1089*9628SMark.Johnson@Sun.COM 		(void) ddi_dma_sync(channel->ic_desc_dma_handle,
1090*9628SMark.Johnson@Sun.COM 		    ring->cr_desc_next << 6, 64, DDI_DMA_SYNC_FORDEV);
10916707Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
10926707Sbrutus 		    (ring->cr_desc_next << 6);
10936707Sbrutus 	}
1094*9628SMark.Johnson@Sun.COM 
1095*9628SMark.Johnson@Sun.COM 	/* Put the descriptors physical address in the previous descriptor */
1096*9628SMark.Johnson@Sun.COM 	/*LINTED:E_TRUE_LOGICAL_EXPR*/
1097*9628SMark.Johnson@Sun.COM 	ASSERT(sizeof (ioat_chan_dca_desc_t) == 64);
1098*9628SMark.Johnson@Sun.COM 
1099*9628SMark.Johnson@Sun.COM 	/* sync the current desc */
1100*9628SMark.Johnson@Sun.COM 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, next_offset, 64,
1101*9628SMark.Johnson@Sun.COM 	    DDI_DMA_SYNC_FORDEV);
1102*9628SMark.Johnson@Sun.COM 
1103*9628SMark.Johnson@Sun.COM 	/* update the previous desc and sync it too */
1104*9628SMark.Johnson@Sun.COM 	saved_prev->dd_next_desc = next_desc_phys;
1105*9628SMark.Johnson@Sun.COM 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, prev_offset, 64,
1106*9628SMark.Johnson@Sun.COM 	    DDI_DMA_SYNC_FORDEV);
11076707Sbrutus }
11086707Sbrutus 
11096707Sbrutus 
11106707Sbrutus /*
11116707Sbrutus  * ioat_cmd_post_copy()
11126707Sbrutus  *
11136707Sbrutus  */
11146707Sbrutus static void
11156707Sbrutus ioat_cmd_post_copy(ioat_channel_ring_t *ring, uint64_t src_addr,
11166707Sbrutus     uint64_t dest_addr, uint32_t size, uint32_t ctrl)
11176707Sbrutus {
1118*9628SMark.Johnson@Sun.COM 	ioat_chan_dma_desc_t *saved_prev;
11196707Sbrutus 	ioat_chan_dma_desc_t *desc;
11206707Sbrutus 	ioat_chan_dma_desc_t *prev;
11216707Sbrutus 	ioat_channel_t channel;
1122*9628SMark.Johnson@Sun.COM 	uint64_t next_desc_phy;
1123*9628SMark.Johnson@Sun.COM 	off_t prev_offset;
1124*9628SMark.Johnson@Sun.COM 	off_t next_offset;
11256707Sbrutus 
11266707Sbrutus 
11276707Sbrutus 	channel = ring->cr_chan;
11286707Sbrutus 	desc = (ioat_chan_dma_desc_t *)&ring->cr_desc[ring->cr_desc_next];
11296707Sbrutus 	prev = (ioat_chan_dma_desc_t *)&ring->cr_desc[ring->cr_desc_prev];
11306707Sbrutus 
11316707Sbrutus 	/* keep track of the number of descs posted for cbv2 */
11326707Sbrutus 	ring->cr_post_cnt++;
11336707Sbrutus 
11346707Sbrutus 	/* write in the DMA desc */
11356707Sbrutus 	desc->dd_ctrl = IOAT_DESC_CTRL_OP_DMA | ctrl;
11366707Sbrutus 	desc->dd_size = size;
11376707Sbrutus 	desc->dd_src_paddr = src_addr;
11386707Sbrutus 	desc->dd_dest_paddr = dest_addr;
11396707Sbrutus 	desc->dd_next_desc = 0x0;
11406707Sbrutus 
1141*9628SMark.Johnson@Sun.COM 	/*
1142*9628SMark.Johnson@Sun.COM 	 * save next desc and prev offset for when we link the two
1143*9628SMark.Johnson@Sun.COM 	 * descriptors together.
1144*9628SMark.Johnson@Sun.COM 	 */
1145*9628SMark.Johnson@Sun.COM 	saved_prev = prev;
1146*9628SMark.Johnson@Sun.COM 	prev_offset = ring->cr_desc_prev << 6;
1147*9628SMark.Johnson@Sun.COM 	next_offset = ring->cr_desc_next << 6;
1148*9628SMark.Johnson@Sun.COM 	next_desc_phy = ring->cr_phys_desc + next_offset;
11496707Sbrutus 
11506707Sbrutus 	/* increment next/gen so it points to the next free desc */
11516707Sbrutus 	ring->cr_desc_prev = ring->cr_desc_next;
11526707Sbrutus 	ring->cr_desc_gen_prev = ring->cr_desc_gen;
11536707Sbrutus 
11546707Sbrutus 	/* increment next/gen so it points to the next free desc */
11556707Sbrutus 	ring->cr_desc_next++;
11566707Sbrutus 	if (ring->cr_desc_next > ring->cr_desc_last) {
11576707Sbrutus 		ring->cr_desc_next = 0;
11586707Sbrutus 		ring->cr_desc_gen++;
11596707Sbrutus 	}
11606707Sbrutus 
11616707Sbrutus 	/*
11626707Sbrutus 	 * if this is CBv2, link the descriptor to an empty descriptor. Since
11636707Sbrutus 	 * we always leave on desc empty to detect full, this works out.
11646707Sbrutus 	 */
11656707Sbrutus 	if (ring->cr_chan->ic_ver == IOAT_CBv2) {
11666707Sbrutus 		desc = (ioat_chan_dma_desc_t *)
11676707Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
11686707Sbrutus 		prev = (ioat_chan_dma_desc_t *)
11696707Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
11706707Sbrutus 		desc->dd_size = 0;
11716707Sbrutus 		desc->dd_ctrl = 0;
11726707Sbrutus 		desc->dd_next_desc = 0x0;
1173*9628SMark.Johnson@Sun.COM 		(void) ddi_dma_sync(channel->ic_desc_dma_handle,
1174*9628SMark.Johnson@Sun.COM 		    ring->cr_desc_next << 6, 64, DDI_DMA_SYNC_FORDEV);
11756707Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
11766707Sbrutus 		    (ring->cr_desc_next << 6);
11776707Sbrutus 	}
1178*9628SMark.Johnson@Sun.COM 
1179*9628SMark.Johnson@Sun.COM 	/* Put the descriptors physical address in the previous descriptor */
1180*9628SMark.Johnson@Sun.COM 	/*LINTED:E_TRUE_LOGICAL_EXPR*/
1181*9628SMark.Johnson@Sun.COM 	ASSERT(sizeof (ioat_chan_dma_desc_t) == 64);
1182*9628SMark.Johnson@Sun.COM 
1183*9628SMark.Johnson@Sun.COM 	/* sync the current desc */
1184*9628SMark.Johnson@Sun.COM 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, next_offset, 64,
1185*9628SMark.Johnson@Sun.COM 	    DDI_DMA_SYNC_FORDEV);
1186*9628SMark.Johnson@Sun.COM 
1187*9628SMark.Johnson@Sun.COM 	/* update the previous desc and sync it too */
1188*9628SMark.Johnson@Sun.COM 	saved_prev->dd_next_desc = next_desc_phy;
1189*9628SMark.Johnson@Sun.COM 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, prev_offset, 64,
1190*9628SMark.Johnson@Sun.COM 	    DDI_DMA_SYNC_FORDEV);
11916707Sbrutus }
11926707Sbrutus 
11936707Sbrutus 
11946707Sbrutus /*
11956707Sbrutus  * ioat_cmd_poll()
11966707Sbrutus  */
11976707Sbrutus int
11986707Sbrutus ioat_cmd_poll(void *private, dcopy_cmd_t cmd)
11996707Sbrutus {
12006707Sbrutus 	ioat_channel_ring_t *ring;
12016707Sbrutus 	ioat_cmd_private_t *priv;
12026707Sbrutus 	ioat_channel_t channel;
12036707Sbrutus 	uint64_t generation;
12046707Sbrutus 	uint64_t last_cmpl;
12056707Sbrutus 
12066707Sbrutus 
12076707Sbrutus 	channel = (ioat_channel_t)private;
12086707Sbrutus 	priv = cmd->dp_private->pr_device_cmd_private;
12096707Sbrutus 
12106707Sbrutus 	ring = channel->ic_ring;
12116707Sbrutus 	ASSERT(ring != NULL);
12126707Sbrutus 
12136707Sbrutus 	mutex_enter(&ring->cr_cmpl_mutex);
12146707Sbrutus 
12156707Sbrutus 	/* if the channel had a fatal failure, fail all polls */
12166707Sbrutus 	if ((channel->ic_channel_state == IOAT_CHANNEL_IN_FAILURE) ||
12176707Sbrutus 	    IOAT_CMPL_FAILED(channel)) {
12186707Sbrutus 		mutex_exit(&ring->cr_cmpl_mutex);
12196707Sbrutus 		return (DCOPY_FAILURE);
12206707Sbrutus 	}
12216707Sbrutus 
12226707Sbrutus 	/*
12236707Sbrutus 	 * if the current completion is the same as the last time we read one,
12246707Sbrutus 	 * post is still pending, nothing further to do. We track completions
12256707Sbrutus 	 * as indexes into the ring since post uses VAs and the H/W returns
12266707Sbrutus 	 * PAs. We grab a snapshot of generation and last_cmpl in the mutex.
12276707Sbrutus 	 */
12286707Sbrutus 	(void) ddi_dma_sync(channel->ic_cmpl_dma_handle, 0, 0,
12296707Sbrutus 	    DDI_DMA_SYNC_FORCPU);
12306707Sbrutus 	last_cmpl = IOAT_CMPL_INDEX(channel);
12316707Sbrutus 	if (last_cmpl != ring->cr_cmpl_last) {
12326707Sbrutus 		/*
12336707Sbrutus 		 * if we wrapped the ring, increment the generation. Store
12346707Sbrutus 		 * the last cmpl. This logic assumes a physically contiguous
12356707Sbrutus 		 * ring.
12366707Sbrutus 		 */
12376707Sbrutus 		if (last_cmpl < ring->cr_cmpl_last) {
12386707Sbrutus 			ring->cr_cmpl_gen++;
12396707Sbrutus 		}
12406707Sbrutus 		ring->cr_cmpl_last = last_cmpl;
12416707Sbrutus 		generation = ring->cr_cmpl_gen;
12426707Sbrutus 
12436707Sbrutus 	} else {
12446707Sbrutus 		generation = ring->cr_cmpl_gen;
12456707Sbrutus 	}
12466707Sbrutus 
12476707Sbrutus 	mutex_exit(&ring->cr_cmpl_mutex);
12486707Sbrutus 
12496707Sbrutus 	/*
12506707Sbrutus 	 * if cmd isn't passed in, well return.  Useful for updating the
12516707Sbrutus 	 * consumer pointer (ring->cr_cmpl_last).
12526707Sbrutus 	 */
12536707Sbrutus 	if (cmd == NULL) {
12546707Sbrutus 		return (DCOPY_PENDING);
12556707Sbrutus 	}
12566707Sbrutus 
12576707Sbrutus 	/*
12586707Sbrutus 	 * if the post's generation is old, this post has completed. No reason
12596707Sbrutus 	 * to go check the last completion. if the generation is the same
12606707Sbrutus 	 * and if the post is before or = to the last completion processed,
12616707Sbrutus 	 * the post has completed.
12626707Sbrutus 	 */
12636707Sbrutus 	if (priv->ip_generation < generation) {
12646707Sbrutus 		return (DCOPY_COMPLETED);
12656707Sbrutus 	} else if ((priv->ip_generation == generation) &&
12666707Sbrutus 	    (priv->ip_index <= last_cmpl)) {
12676707Sbrutus 		return (DCOPY_COMPLETED);
12686707Sbrutus 	}
12696707Sbrutus 
12706707Sbrutus 	return (DCOPY_PENDING);
12716707Sbrutus }
12726707Sbrutus 
12736707Sbrutus 
12746707Sbrutus /*
12756707Sbrutus  * ioat_ring_reserve()
12766707Sbrutus  */
12776707Sbrutus int
12786707Sbrutus ioat_ring_reserve(ioat_channel_t channel, ioat_channel_ring_t *ring,
12796707Sbrutus     dcopy_cmd_t cmd)
12806707Sbrutus {
12816707Sbrutus 	uint64_t dest_addr;
12826707Sbrutus 	uint32_t dest_size;
12836707Sbrutus 	uint64_t src_addr;
12846707Sbrutus 	uint32_t src_size;
12856707Sbrutus 	size_t xfer_size;
12866707Sbrutus 	uint64_t desc;
12876707Sbrutus 	int num_desc;
12886707Sbrutus 	size_t size;
12896707Sbrutus 	int i;
12906707Sbrutus 
12916707Sbrutus 
12926707Sbrutus 	/*
12936707Sbrutus 	 * figure out how many descriptors we need. This can include a dca
12946707Sbrutus 	 * desc and multiple desc for a dma copy.
12956707Sbrutus 	 */
12966707Sbrutus 	num_desc = 0;
12976707Sbrutus 	if ((channel->ic_ver == IOAT_CBv2) &&
12986707Sbrutus 	    (cmd->dp_flags & DCOPY_CMD_DCA)) {
12996707Sbrutus 		num_desc++;
13006707Sbrutus 	}
13016707Sbrutus 	src_addr = cmd->dp.copy.cc_source;
13026707Sbrutus 	dest_addr = cmd->dp.copy.cc_dest;
13036707Sbrutus 	size = cmd->dp.copy.cc_size;
13046707Sbrutus 	while (size > 0) {
13056707Sbrutus 		num_desc++;
13066707Sbrutus 
13076707Sbrutus 		/* adjust for any offset into the page */
13086707Sbrutus 		if ((src_addr & PAGEOFFSET) == 0) {
13096707Sbrutus 			src_size = PAGESIZE;
13106707Sbrutus 		} else {
13116707Sbrutus 			src_size = PAGESIZE - (src_addr & PAGEOFFSET);
13126707Sbrutus 		}
13136707Sbrutus 		if ((dest_addr & PAGEOFFSET) == 0) {
13146707Sbrutus 			dest_size = PAGESIZE;
13156707Sbrutus 		} else {
13166707Sbrutus 			dest_size = PAGESIZE - (dest_addr & PAGEOFFSET);
13176707Sbrutus 		}
13186707Sbrutus 
13196707Sbrutus 		/* take the smallest of the three */
13206707Sbrutus 		xfer_size = MIN(src_size, dest_size);
13216707Sbrutus 		xfer_size = MIN(xfer_size, size);
13226707Sbrutus 
13236707Sbrutus 		/* go to the next page */
13246707Sbrutus 		src_addr += xfer_size;
13256707Sbrutus 		dest_addr += xfer_size;
13266707Sbrutus 		size -= xfer_size;
13276707Sbrutus 	}
13286707Sbrutus 
13296707Sbrutus 	/* Make sure we have space for these descriptors */
13306707Sbrutus 	desc = ring->cr_desc_next;
13316707Sbrutus 	for (i = 0; i < num_desc; i++) {
13326707Sbrutus 
13336707Sbrutus 		/*
13346707Sbrutus 		 * if this is the last descriptor in the ring, see if the
13356707Sbrutus 		 * last completed descriptor is #0.
13366707Sbrutus 		 */
13376707Sbrutus 		if (desc == ring->cr_desc_last) {
13386707Sbrutus 			if (ring->cr_cmpl_last == 0) {
13396707Sbrutus 				/*
13406707Sbrutus 				 * if we think the ring is full, update where
13416707Sbrutus 				 * the H/W really is and check for full again.
13426707Sbrutus 				 */
13436707Sbrutus 				(void) ioat_cmd_poll(channel, NULL);
13446707Sbrutus 				if (ring->cr_cmpl_last == 0) {
13456707Sbrutus 					return (DCOPY_NORESOURCES);
13466707Sbrutus 				}
13476707Sbrutus 			}
13486707Sbrutus 
13496707Sbrutus 			/*
13506707Sbrutus 			 * go to the next descriptor which is zero in this
13516707Sbrutus 			 * case.
13526707Sbrutus 			 */
13536707Sbrutus 			desc = 0;
13546707Sbrutus 
13556707Sbrutus 		/*
13566707Sbrutus 		 * if this is not the last descriptor in the ring, see if
13576707Sbrutus 		 * the last completion we saw was the next descriptor.
13586707Sbrutus 		 */
13596707Sbrutus 		} else {
13606707Sbrutus 			if ((desc + 1) == ring->cr_cmpl_last) {
13616707Sbrutus 				/*
13626707Sbrutus 				 * if we think the ring is full, update where
13636707Sbrutus 				 * the H/W really is and check for full again.
13646707Sbrutus 				 */
13656707Sbrutus 				(void) ioat_cmd_poll(channel, NULL);
13666707Sbrutus 				if ((desc + 1) == ring->cr_cmpl_last) {
13676707Sbrutus 					return (DCOPY_NORESOURCES);
13686707Sbrutus 				}
13696707Sbrutus 			}
13706707Sbrutus 
13716707Sbrutus 			/* go to the next descriptor */
13726707Sbrutus 			desc++;
13736707Sbrutus 		}
13746707Sbrutus 	}
13756707Sbrutus 
13766707Sbrutus 	return (DCOPY_SUCCESS);
13776707Sbrutus }
1378