xref: /onnv-gate/usr/src/uts/i86pc/io/ioat/ioat_chan.c (revision 9638:8e20e33b68dc)
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 
27*9638SRandy.Fishel@Sun.COM /*
28*9638SRandy.Fishel@Sun.COM  * Copyright (c) 2009, Intel Corporation.
29*9638SRandy.Fishel@Sun.COM  * All rights reserved.
30*9638SRandy.Fishel@Sun.COM  */
31*9638SRandy.Fishel@Sun.COM 
326707Sbrutus #include <sys/errno.h>
336707Sbrutus #include <sys/types.h>
346707Sbrutus #include <sys/conf.h>
356707Sbrutus #include <sys/kmem.h>
366707Sbrutus #include <sys/ddi.h>
376707Sbrutus #include <sys/stat.h>
386707Sbrutus #include <sys/sunddi.h>
396707Sbrutus #include <sys/file.h>
406707Sbrutus #include <sys/open.h>
416707Sbrutus #include <sys/modctl.h>
426707Sbrutus #include <sys/ddi_impldefs.h>
436707Sbrutus #include <sys/sysmacros.h>
446707Sbrutus #include <vm/hat.h>
456707Sbrutus #include <vm/as.h>
466707Sbrutus #include <sys/mach_mmu.h>
476707Sbrutus #ifdef __xpv
486707Sbrutus #include <sys/hypervisor.h>
496707Sbrutus #endif
506707Sbrutus 
516707Sbrutus #include <sys/ioat.h>
526707Sbrutus 
536707Sbrutus 
546707Sbrutus extern ddi_device_acc_attr_t ioat_acc_attr;
556707Sbrutus 
566707Sbrutus /* dma attr for the descriptor rings */
576707Sbrutus ddi_dma_attr_t ioat_desc_dma_attr = {
586707Sbrutus 	DMA_ATTR_V0,		/* dma_attr_version */
596707Sbrutus 	0x0,			/* dma_attr_addr_lo */
606707Sbrutus 	0xffffffffffffffff,	/* dma_attr_addr_hi */
616707Sbrutus 	0xffffffff,		/* dma_attr_count_max */
626707Sbrutus 	0x1000,			/* dma_attr_align */
636707Sbrutus 	0x1,			/* dma_attr_burstsizes */
646707Sbrutus 	0x1,			/* dma_attr_minxfer */
656707Sbrutus 	0xffffffff,		/* dma_attr_maxxfer */
666707Sbrutus 	0xffffffff,		/* dma_attr_seg */
676707Sbrutus 	0x1,			/* dma_attr_sgllen */
686707Sbrutus 	0x1,			/* dma_attr_granular */
696707Sbrutus 	0x0,			/* dma_attr_flags */
706707Sbrutus };
716707Sbrutus 
726707Sbrutus /* dma attr for the completion buffers */
736707Sbrutus ddi_dma_attr_t ioat_cmpl_dma_attr = {
746707Sbrutus 	DMA_ATTR_V0,		/* dma_attr_version */
756707Sbrutus 	0x0,			/* dma_attr_addr_lo */
766707Sbrutus 	0xffffffffffffffff,	/* dma_attr_addr_hi */
776707Sbrutus 	0xffffffff,		/* dma_attr_count_max */
786707Sbrutus 	0x40,			/* dma_attr_align */
796707Sbrutus 	0x1,			/* dma_attr_burstsizes */
806707Sbrutus 	0x1,			/* dma_attr_minxfer */
816707Sbrutus 	0xffffffff,		/* dma_attr_maxxfer */
826707Sbrutus 	0xffffffff,		/* dma_attr_seg */
836707Sbrutus 	0x1,			/* dma_attr_sgllen */
846707Sbrutus 	0x1,			/* dma_attr_granular */
856707Sbrutus 	0x0,			/* dma_attr_flags */
866707Sbrutus };
876707Sbrutus 
886707Sbrutus static int ioat_completion_alloc(ioat_channel_t channel);
896707Sbrutus static void ioat_completion_free(ioat_channel_t channel);
906707Sbrutus static void ioat_channel_start(ioat_channel_t channel);
916707Sbrutus static void ioat_channel_reset(ioat_channel_t channel);
926707Sbrutus 
936707Sbrutus int ioat_ring_alloc(ioat_channel_t channel, uint_t desc_cnt);
946707Sbrutus void ioat_ring_free(ioat_channel_t channel);
956707Sbrutus void ioat_ring_seed(ioat_channel_t channel, ioat_chan_dma_desc_t *desc);
966707Sbrutus int ioat_ring_reserve(ioat_channel_t channel, ioat_channel_ring_t *ring,
976707Sbrutus     dcopy_cmd_t cmd);
986707Sbrutus 
996707Sbrutus static void ioat_cmd_post_copy(ioat_channel_ring_t *ring, uint64_t src_addr,
1006707Sbrutus     uint64_t dest_addr, uint32_t size, uint32_t ctrl);
1016707Sbrutus static void ioat_cmd_post_dca(ioat_channel_ring_t *ring, uint32_t dca_id);
1026707Sbrutus 
1036707Sbrutus 
1046707Sbrutus /*
1056707Sbrutus  * ioat_channel_init()
1066707Sbrutus  */
1076707Sbrutus int
ioat_channel_init(ioat_state_t * state)1086707Sbrutus ioat_channel_init(ioat_state_t *state)
1096707Sbrutus {
1106707Sbrutus 	int i;
1116707Sbrutus 
1126707Sbrutus 	/*
1136707Sbrutus 	 * initialize each dma channel's state which doesn't change across
1146707Sbrutus 	 * channel alloc/free.
1156707Sbrutus 	 */
1166707Sbrutus 	state->is_chansize = sizeof (struct ioat_channel_s) *
1176707Sbrutus 	    state->is_num_channels;
1186707Sbrutus 	state->is_channel = kmem_zalloc(state->is_chansize, KM_SLEEP);
1196707Sbrutus 	for (i = 0; i < state->is_num_channels; i++) {
1206707Sbrutus 		state->is_channel[i].ic_state = state;
1216707Sbrutus 		state->is_channel[i].ic_regs = (uint8_t *)
1226707Sbrutus 		    ((uintptr_t)state->is_genregs +
1236707Sbrutus 		    (uintptr_t)(IOAT_CHANNELREG_OFFSET * (i + 1)));
1246707Sbrutus 	}
1256707Sbrutus 
1266707Sbrutus 	/* initial the allocator (from 0 to state->is_num_channels) */
1276707Sbrutus 	ioat_rs_init(state, 0, state->is_num_channels, &state->is_channel_rs);
1286707Sbrutus 
1296707Sbrutus 	return (DDI_SUCCESS);
1306707Sbrutus }
1316707Sbrutus 
1326707Sbrutus 
1336707Sbrutus /*
1346707Sbrutus  * ioat_channel_fini()
1356707Sbrutus  */
1366707Sbrutus void
ioat_channel_fini(ioat_state_t * state)1376707Sbrutus ioat_channel_fini(ioat_state_t *state)
1386707Sbrutus {
1396707Sbrutus 	ioat_rs_fini(&state->is_channel_rs);
1406707Sbrutus 	kmem_free(state->is_channel, state->is_chansize);
1416707Sbrutus }
1426707Sbrutus 
1436707Sbrutus 
1446707Sbrutus /*
1456707Sbrutus  * ioat_channel_alloc()
1466707Sbrutus  *   NOTE: We intentionaly don't handle DCOPY_SLEEP (if no channels are
1476707Sbrutus  *	available)
1486707Sbrutus  */
1496707Sbrutus /*ARGSUSED*/
1506707Sbrutus int
ioat_channel_alloc(void * device_private,dcopy_handle_t handle,int flags,uint_t size,dcopy_query_channel_t * info,void * channel_private)1516707Sbrutus ioat_channel_alloc(void *device_private, dcopy_handle_t handle, int flags,
1526707Sbrutus     uint_t size, dcopy_query_channel_t *info, void *channel_private)
1536707Sbrutus {
1546707Sbrutus #define	CHANSTRSIZE	20
1556707Sbrutus 	struct ioat_channel_s *channel;
1566707Sbrutus 	char chanstr[CHANSTRSIZE];
1576707Sbrutus 	ioat_channel_t *chan;
1586707Sbrutus 	ioat_state_t *state;
1596707Sbrutus 	size_t cmd_size;
1606707Sbrutus 	uint_t chan_num;
1616707Sbrutus 	uint32_t estat;
1626707Sbrutus 	int e;
1636707Sbrutus 
1646707Sbrutus 
1656707Sbrutus 	state = (ioat_state_t *)device_private;
1666707Sbrutus 	chan = (ioat_channel_t *)channel_private;
1676707Sbrutus 
1686707Sbrutus 	/* allocate a H/W channel */
1696707Sbrutus 	e = ioat_rs_alloc(state->is_channel_rs, &chan_num);
1706707Sbrutus 	if (e != DDI_SUCCESS) {
1716707Sbrutus 		return (DCOPY_NORESOURCES);
1726707Sbrutus 	}
1736707Sbrutus 
1746707Sbrutus 	channel = &state->is_channel[chan_num];
1756707Sbrutus 	channel->ic_inuse = B_TRUE;
1766707Sbrutus 	channel->ic_chan_num = chan_num;
1776707Sbrutus 	channel->ic_ver = state->is_ver;
1786707Sbrutus 	channel->ic_dca_active = B_FALSE;
1796707Sbrutus 	channel->ic_channel_state = IOAT_CHANNEL_OK;
1806707Sbrutus 	channel->ic_dcopy_handle = handle;
1816707Sbrutus 
1826707Sbrutus #ifdef	DEBUG
1836707Sbrutus 	{
1846707Sbrutus 		/* if we're cbv2, verify that the V2 compatibility bit is set */
1856707Sbrutus 		uint16_t reg;
1866707Sbrutus 		if (channel->ic_ver == IOAT_CBv2) {
1876707Sbrutus 			reg = ddi_get16(state->is_reg_handle,
1886707Sbrutus 			    (uint16_t *)&channel->ic_regs[IOAT_CHAN_COMP]);
1896707Sbrutus 			ASSERT(reg & 0x2);
1906707Sbrutus 		}
1916707Sbrutus 	}
1926707Sbrutus #endif
1936707Sbrutus 
1946707Sbrutus 	/*
1956707Sbrutus 	 * Configure DMA channel
1966707Sbrutus 	 *   Channel In Use
1976707Sbrutus 	 *   Error Interrupt Enable
1986707Sbrutus 	 *   Any Error Abort Enable
1996707Sbrutus 	 *   Error Completion Enable
2006707Sbrutus 	 */
2016707Sbrutus 	ddi_put16(state->is_reg_handle,
2026707Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x011C);
2036707Sbrutus 
2046707Sbrutus 	/* check channel error register, clear any errors */
2056707Sbrutus 	estat = ddi_get32(state->is_reg_handle,
2066707Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
2076707Sbrutus 	if (estat != 0) {
2086707Sbrutus #ifdef	DEBUG
2096707Sbrutus 		cmn_err(CE_CONT, "cleared errors (0x%x) before channel (%d) "
2106707Sbrutus 		    "enable\n", estat, channel->ic_chan_num);
2116707Sbrutus #endif
2126707Sbrutus 		ddi_put32(state->is_reg_handle,
2136707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR], estat);
2146707Sbrutus 	}
2156707Sbrutus 
2166707Sbrutus 	/* allocate and initialize the descriptor buf */
2176707Sbrutus 	e = ioat_ring_alloc(channel, size);
2186707Sbrutus 	if (e != DDI_SUCCESS) {
2196707Sbrutus 		goto chinitfail_desc_alloc;
2206707Sbrutus 	}
2216707Sbrutus 
2226707Sbrutus 	/* allocate and initialize the completion space */
2236707Sbrutus 	e = ioat_completion_alloc(channel);
2246707Sbrutus 	if (e != DDI_SUCCESS) {
2256707Sbrutus 		goto chinitfail_completion_alloc;
2266707Sbrutus 	}
2276707Sbrutus 
2286707Sbrutus 	/* setup kmem_cache for commands */
2296707Sbrutus 	cmd_size = sizeof (struct dcopy_cmd_s) +
2306707Sbrutus 	    sizeof (struct dcopy_cmd_priv_s) +
2316707Sbrutus 	    sizeof (struct ioat_cmd_private_s);
2326707Sbrutus 	(void) snprintf(chanstr, CHANSTRSIZE, "ioat%dchan%dcmd",
2336707Sbrutus 	    state->is_instance, channel->ic_chan_num);
2346707Sbrutus 	channel->ic_cmd_cache = kmem_cache_create(chanstr, cmd_size, 64,
2356707Sbrutus 	    NULL, NULL, NULL, NULL, NULL, 0);
2366707Sbrutus 	if (channel->ic_cmd_cache == NULL) {
2376707Sbrutus 		goto chinitfail_kmem_cache;
2386707Sbrutus 	}
2396707Sbrutus 
2406707Sbrutus 	/* start-up the channel */
2416707Sbrutus 	ioat_channel_start(channel);
2426707Sbrutus 
2436707Sbrutus 	/* fill in the channel info returned to dcopy */
2446707Sbrutus 	info->qc_version = DCOPY_QUERY_CHANNEL_V0;
2456707Sbrutus 	info->qc_id = state->is_deviceinfo.di_id;
2466707Sbrutus 	info->qc_capabilities = (uint64_t)state->is_capabilities;
2476707Sbrutus 	info->qc_channel_size = (uint64_t)size;
2486707Sbrutus 	info->qc_chan_num = (uint64_t)channel->ic_chan_num;
2496707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
2506707Sbrutus 		info->qc_dca_supported = B_FALSE;
2516707Sbrutus 	} else {
2526707Sbrutus 		if (info->qc_capabilities & IOAT_DMACAP_DCA) {
2536707Sbrutus 			info->qc_dca_supported = B_TRUE;
2546707Sbrutus 		} else {
2556707Sbrutus 			info->qc_dca_supported = B_FALSE;
2566707Sbrutus 		}
2576707Sbrutus 	}
2586707Sbrutus 
2596707Sbrutus 	*chan = channel;
2606707Sbrutus 
2616707Sbrutus 	return (DCOPY_SUCCESS);
2626707Sbrutus 
2636707Sbrutus chinitfail_kmem_cache:
2646707Sbrutus 	ioat_completion_free(channel);
2656707Sbrutus chinitfail_completion_alloc:
2666707Sbrutus 	ioat_ring_free(channel);
2676707Sbrutus chinitfail_desc_alloc:
2686707Sbrutus 	return (DCOPY_FAILURE);
2696707Sbrutus }
2706707Sbrutus 
2716707Sbrutus 
2726707Sbrutus /*
2736707Sbrutus  * ioat_channel_suspend()
2746707Sbrutus  */
2756707Sbrutus /*ARGSUSED*/
2766707Sbrutus void
ioat_channel_suspend(ioat_state_t * state)2776707Sbrutus ioat_channel_suspend(ioat_state_t *state)
2786707Sbrutus {
2796707Sbrutus 	/*
2806707Sbrutus 	 * normally you would disable interrupts and reset the H/W here. But
2816707Sbrutus 	 * since the suspend framework doesn't know who is using us, it may
2826707Sbrutus 	 * not suspend their I/O before us.  Since we won't actively be doing
2836707Sbrutus 	 * any DMA or interrupts unless someone asks us to, it's safe to not
2846707Sbrutus 	 * do anything here.
2856707Sbrutus 	 */
2866707Sbrutus }
2876707Sbrutus 
2886707Sbrutus 
2896707Sbrutus /*
2906707Sbrutus  * ioat_channel_resume()
2916707Sbrutus  */
2926707Sbrutus int
ioat_channel_resume(ioat_state_t * state)2936707Sbrutus ioat_channel_resume(ioat_state_t *state)
2946707Sbrutus {
2956707Sbrutus 	ioat_channel_ring_t *ring;
2966707Sbrutus 	ioat_channel_t channel;
2976707Sbrutus 	uint32_t estat;
2986707Sbrutus 	int i;
2996707Sbrutus 
3006707Sbrutus 
3016707Sbrutus 	for (i = 0; i < state->is_num_channels; i++) {
3026707Sbrutus 		channel = &state->is_channel[i];
3036707Sbrutus 		ring = channel->ic_ring;
3046707Sbrutus 
3056707Sbrutus 		if (!channel->ic_inuse) {
3066707Sbrutus 			continue;
3076707Sbrutus 		}
3086707Sbrutus 
3096707Sbrutus 		/*
3106707Sbrutus 		 * Configure DMA channel
3116707Sbrutus 		 *   Channel In Use
3126707Sbrutus 		 *   Error Interrupt Enable
3136707Sbrutus 		 *   Any Error Abort Enable
3146707Sbrutus 		 *   Error Completion Enable
3156707Sbrutus 		 */
3166707Sbrutus 		ddi_put16(state->is_reg_handle,
3176707Sbrutus 		    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x011C);
3186707Sbrutus 
3196707Sbrutus 		/* check channel error register, clear any errors */
3206707Sbrutus 		estat = ddi_get32(state->is_reg_handle,
3216707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
3226707Sbrutus 		if (estat != 0) {
3236707Sbrutus #ifdef	DEBUG
3246707Sbrutus 			cmn_err(CE_CONT, "cleared errors (0x%x) before channel"
3256707Sbrutus 			    " (%d) enable\n", estat, channel->ic_chan_num);
3266707Sbrutus #endif
3276707Sbrutus 			ddi_put32(state->is_reg_handle,
3286707Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR],
3296707Sbrutus 			    estat);
3306707Sbrutus 		}
3316707Sbrutus 
3326707Sbrutus 		/* Re-initialize the ring */
3336707Sbrutus 		bzero(ring->cr_desc, channel->ic_desc_alloc_size);
3346707Sbrutus 		/* write the physical address into the chain address register */
3356707Sbrutus 		if (channel->ic_ver == IOAT_CBv1) {
3366707Sbrutus 			ddi_put32(state->is_reg_handle,
3376707Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO],
3386707Sbrutus 			    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
3396707Sbrutus 			ddi_put32(state->is_reg_handle,
3406707Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI],
3416707Sbrutus 			    (uint32_t)(ring->cr_phys_desc >> 32));
3426707Sbrutus 		} else {
3436707Sbrutus 			ASSERT(channel->ic_ver == IOAT_CBv2);
3446707Sbrutus 			ddi_put32(state->is_reg_handle,
3456707Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO],
3466707Sbrutus 			    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
3476707Sbrutus 			ddi_put32(state->is_reg_handle,
3486707Sbrutus 			    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI],
3496707Sbrutus 			    (uint32_t)(ring->cr_phys_desc >> 32));
3506707Sbrutus 		}
3516707Sbrutus 
3526707Sbrutus 		/* re-initialize the completion buffer */
3536707Sbrutus 		bzero((void *)channel->ic_cmpl, channel->ic_cmpl_alloc_size);
3546707Sbrutus 		/* write the phys addr into the completion address register */
3556707Sbrutus 		ddi_put32(state->is_reg_handle,
3566707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO],
3576707Sbrutus 		    (uint32_t)(channel->ic_phys_cmpl & 0xffffffff));
3586707Sbrutus 		ddi_put32(state->is_reg_handle,
3596707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI],
3606707Sbrutus 		    (uint32_t)(channel->ic_phys_cmpl >> 32));
3616707Sbrutus 
3626707Sbrutus 		/* start-up the channel */
3636707Sbrutus 		ioat_channel_start(channel);
3646707Sbrutus 
3656707Sbrutus 	}
3666707Sbrutus 
3676707Sbrutus 	return (DDI_SUCCESS);
3686707Sbrutus }
3696707Sbrutus 
3707656SSherry.Moore@Sun.COM /*
3717656SSherry.Moore@Sun.COM  * quiesce(9E) entry point.
3727656SSherry.Moore@Sun.COM  *
3737656SSherry.Moore@Sun.COM  * This function is called when the system is single-threaded at high
3747656SSherry.Moore@Sun.COM  * PIL with preemption disabled. Therefore, this function must not be
3757656SSherry.Moore@Sun.COM  * blocked.
3767656SSherry.Moore@Sun.COM  *
3777656SSherry.Moore@Sun.COM  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
3787656SSherry.Moore@Sun.COM  * DDI_FAILURE indicates an error condition and should almost never happen.
3797656SSherry.Moore@Sun.COM  */
3807656SSherry.Moore@Sun.COM void
ioat_channel_quiesce(ioat_state_t * state)3817656SSherry.Moore@Sun.COM ioat_channel_quiesce(ioat_state_t *state)
3827656SSherry.Moore@Sun.COM {
3837656SSherry.Moore@Sun.COM 	int i;
3847656SSherry.Moore@Sun.COM 
3857656SSherry.Moore@Sun.COM 	/*
3867656SSherry.Moore@Sun.COM 	 * Walk through all channels and quiesce
3877656SSherry.Moore@Sun.COM 	 */
3887656SSherry.Moore@Sun.COM 	for (i = 0; i < state->is_num_channels; i++) {
3897656SSherry.Moore@Sun.COM 
3907656SSherry.Moore@Sun.COM 		ioat_channel_t	channel = state->is_channel + i;
3917656SSherry.Moore@Sun.COM 
3927656SSherry.Moore@Sun.COM 		if (!channel->ic_inuse)
3937656SSherry.Moore@Sun.COM 			continue;
3947656SSherry.Moore@Sun.COM 
3957656SSherry.Moore@Sun.COM 		/* disable the interrupts */
3967656SSherry.Moore@Sun.COM 		ddi_put16(state->is_reg_handle,
3977656SSherry.Moore@Sun.COM 		    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL],
3987656SSherry.Moore@Sun.COM 		    0x0);
3997656SSherry.Moore@Sun.COM 
4007656SSherry.Moore@Sun.COM 		ioat_channel_reset(channel);
4017656SSherry.Moore@Sun.COM 	}
4027656SSherry.Moore@Sun.COM }
4037656SSherry.Moore@Sun.COM 
4046707Sbrutus 
4056707Sbrutus /*
4066707Sbrutus  * ioat_channel_free()
4076707Sbrutus  */
4086707Sbrutus void
ioat_channel_free(void * channel_private)4096707Sbrutus ioat_channel_free(void *channel_private)
4106707Sbrutus {
4116707Sbrutus 	struct ioat_channel_s *channel;
4126707Sbrutus 	ioat_channel_t *chan;
4136707Sbrutus 	ioat_state_t *state;
4146707Sbrutus 	uint_t chan_num;
4156707Sbrutus 
4166707Sbrutus 
4176707Sbrutus 	chan = (ioat_channel_t *)channel_private;
4186707Sbrutus 	channel = *chan;
4196707Sbrutus 
4206707Sbrutus 	state = channel->ic_state;
4216707Sbrutus 	chan_num = channel->ic_chan_num;
4226707Sbrutus 
4236707Sbrutus 	/* disable the interrupts */
4246707Sbrutus 	ddi_put16(state->is_reg_handle,
4256707Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], 0x0);
4266707Sbrutus 
4276707Sbrutus 	ioat_channel_reset(channel);
4286707Sbrutus 
4296707Sbrutus 	/* cleanup command cache */
4306707Sbrutus 	kmem_cache_destroy(channel->ic_cmd_cache);
4316707Sbrutus 
4326707Sbrutus 	/* clean-up/free-up the completion space and descriptors */
4336707Sbrutus 	ioat_completion_free(channel);
4346707Sbrutus 	ioat_ring_free(channel);
4356707Sbrutus 
4366707Sbrutus 	channel->ic_inuse = B_FALSE;
4376707Sbrutus 
4386707Sbrutus 	/* free the H/W DMA engine */
4396707Sbrutus 	ioat_rs_free(state->is_channel_rs, chan_num);
4406707Sbrutus 
4416707Sbrutus 	*chan = NULL;
4426707Sbrutus }
4436707Sbrutus 
4446707Sbrutus 
4456707Sbrutus /*
4466707Sbrutus  * ioat_channel_intr()
4476707Sbrutus  */
4486707Sbrutus void
ioat_channel_intr(ioat_channel_t channel)4496707Sbrutus ioat_channel_intr(ioat_channel_t channel)
4506707Sbrutus {
4516707Sbrutus 	ioat_state_t *state;
4526707Sbrutus 	uint16_t chanctrl;
4536707Sbrutus 	uint32_t chanerr;
4546707Sbrutus 	uint32_t status;
4556707Sbrutus 
4566707Sbrutus 
4576707Sbrutus 	state = channel->ic_state;
4586707Sbrutus 
4596707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
4606707Sbrutus 		status = ddi_get32(state->is_reg_handle,
4616707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_STS_LO]);
4626707Sbrutus 	} else {
4636707Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
4646707Sbrutus 		status = ddi_get32(state->is_reg_handle,
4656707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_STS_LO]);
4666707Sbrutus 	}
4676707Sbrutus 
4686707Sbrutus 	/* if that status isn't ACTIVE or IDLE, the channel has failed */
4696707Sbrutus 	if (status & IOAT_CHAN_STS_FAIL_MASK) {
4706707Sbrutus 		chanerr = ddi_get32(state->is_reg_handle,
4716707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_CHAN_ERR]);
4726707Sbrutus 		cmn_err(CE_WARN, "channel(%d) fatal failure! "
4736707Sbrutus 		    "chanstat_lo=0x%X; chanerr=0x%X\n",
4746707Sbrutus 		    channel->ic_chan_num, status, chanerr);
4756707Sbrutus 		channel->ic_channel_state = IOAT_CHANNEL_IN_FAILURE;
4766707Sbrutus 		ioat_channel_reset(channel);
4776707Sbrutus 
4786707Sbrutus 		return;
4796707Sbrutus 	}
4806707Sbrutus 
4816707Sbrutus 	/*
4826707Sbrutus 	 * clear interrupt disable bit if set (it's a RW1C). Read it back to
4836707Sbrutus 	 * ensure the write completes.
4846707Sbrutus 	 */
4856707Sbrutus 	chanctrl = ddi_get16(state->is_reg_handle,
4866707Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL]);
4876707Sbrutus 	ddi_put16(state->is_reg_handle,
4886707Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL], chanctrl);
4896707Sbrutus 	(void) ddi_get16(state->is_reg_handle,
4906707Sbrutus 	    (uint16_t *)&channel->ic_regs[IOAT_CHAN_CTL]);
4916707Sbrutus 
4926707Sbrutus 	/* tell dcopy we have seen a completion on this channel */
4936707Sbrutus 	dcopy_device_channel_notify(channel->ic_dcopy_handle, DCOPY_COMPLETION);
4946707Sbrutus }
4956707Sbrutus 
4966707Sbrutus 
4976707Sbrutus /*
4986707Sbrutus  * ioat_channel_start()
4996707Sbrutus  */
5006707Sbrutus void
ioat_channel_start(ioat_channel_t channel)5016707Sbrutus ioat_channel_start(ioat_channel_t channel)
5026707Sbrutus {
5036707Sbrutus 	ioat_chan_dma_desc_t desc;
5046707Sbrutus 
5056707Sbrutus 	/* set the first descriptor up as a NULL descriptor */
5066707Sbrutus 	bzero(&desc, sizeof (desc));
5076707Sbrutus 	desc.dd_size = 0;
5086707Sbrutus 	desc.dd_ctrl = IOAT_DESC_CTRL_OP_DMA | IOAT_DESC_DMACTRL_NULL |
5096707Sbrutus 	    IOAT_DESC_CTRL_CMPL;
5106707Sbrutus 	desc.dd_next_desc = 0x0;
5116707Sbrutus 
5126707Sbrutus 	/* setup the very first descriptor */
5136707Sbrutus 	ioat_ring_seed(channel, &desc);
5146707Sbrutus }
5156707Sbrutus 
5166707Sbrutus 
5176707Sbrutus /*
5186707Sbrutus  * ioat_channel_reset()
5196707Sbrutus  */
5206707Sbrutus void
ioat_channel_reset(ioat_channel_t channel)5216707Sbrutus ioat_channel_reset(ioat_channel_t channel)
5226707Sbrutus {
5236707Sbrutus 	ioat_state_t *state;
5246707Sbrutus 
5256707Sbrutus 	state = channel->ic_state;
5266707Sbrutus 
5276707Sbrutus 	/* hit the reset bit */
5286707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
5296707Sbrutus 		ddi_put8(state->is_reg_handle,
5306707Sbrutus 		    &channel->ic_regs[IOAT_V1_CHAN_CMD], 0x20);
5316707Sbrutus 	} else {
5326707Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
5336707Sbrutus 		ddi_put8(state->is_reg_handle,
5346707Sbrutus 		    &channel->ic_regs[IOAT_V2_CHAN_CMD], 0x20);
5356707Sbrutus 	}
5366707Sbrutus }
5376707Sbrutus 
5386707Sbrutus 
5396707Sbrutus /*
5406707Sbrutus  * ioat_completion_alloc()
5416707Sbrutus  */
5426707Sbrutus int
ioat_completion_alloc(ioat_channel_t channel)5436707Sbrutus ioat_completion_alloc(ioat_channel_t channel)
5446707Sbrutus {
5456707Sbrutus 	ioat_state_t *state;
5466707Sbrutus 	size_t real_length;
5476707Sbrutus 	uint_t cookie_cnt;
5486707Sbrutus 	int e;
5496707Sbrutus 
5506707Sbrutus 
5516707Sbrutus 	state = channel->ic_state;
5526707Sbrutus 
5536707Sbrutus 	/*
5546707Sbrutus 	 * allocate memory for the completion status, zero it out, and get
5556707Sbrutus 	 * the paddr. We'll allocate a physically contiguous cache line.
5566707Sbrutus 	 */
5576707Sbrutus 	e = ddi_dma_alloc_handle(state->is_dip, &ioat_cmpl_dma_attr,
5586707Sbrutus 	    DDI_DMA_SLEEP, NULL, &channel->ic_cmpl_dma_handle);
5596707Sbrutus 	if (e != DDI_SUCCESS) {
5606707Sbrutus 		goto cmplallocfail_alloc_handle;
5616707Sbrutus 	}
5626707Sbrutus 	channel->ic_cmpl_alloc_size = 64;
5636707Sbrutus 	e = ddi_dma_mem_alloc(channel->ic_cmpl_dma_handle,
5646707Sbrutus 	    channel->ic_cmpl_alloc_size, &ioat_acc_attr,
5656707Sbrutus 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
5666707Sbrutus 	    (caddr_t *)&channel->ic_cmpl, &real_length,
5676707Sbrutus 	    &channel->ic_cmpl_handle);
5686707Sbrutus 	if (e != DDI_SUCCESS) {
5696707Sbrutus 		goto cmplallocfail_mem_alloc;
5706707Sbrutus 	}
5716707Sbrutus 	bzero((void *)channel->ic_cmpl, channel->ic_cmpl_alloc_size);
5726707Sbrutus 	e = ddi_dma_addr_bind_handle(channel->ic_cmpl_dma_handle, NULL,
5736707Sbrutus 	    (caddr_t)channel->ic_cmpl, channel->ic_cmpl_alloc_size,
5746707Sbrutus 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
5756707Sbrutus 	    &channel->ic_cmpl_cookie, &cookie_cnt);
5766707Sbrutus 	if (e != DDI_SUCCESS) {
5776707Sbrutus 		goto cmplallocfail_addr_bind;
5786707Sbrutus 	}
5796707Sbrutus 	ASSERT(cookie_cnt == 1);
5806707Sbrutus 	ASSERT(channel->ic_cmpl_cookie.dmac_size ==
5816707Sbrutus 	    channel->ic_cmpl_alloc_size);
5826707Sbrutus 	channel->ic_phys_cmpl = channel->ic_cmpl_cookie.dmac_laddress;
5836707Sbrutus 
5846707Sbrutus 	/* write the physical address into the completion address register */
5856707Sbrutus 	ddi_put32(state->is_reg_handle,
5866707Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO],
5876707Sbrutus 	    (uint32_t)(channel->ic_phys_cmpl & 0xffffffff));
5886707Sbrutus 	ddi_put32(state->is_reg_handle,
5896707Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI],
5906707Sbrutus 	    (uint32_t)(channel->ic_phys_cmpl >> 32));
5916707Sbrutus 
5926707Sbrutus 	return (DDI_SUCCESS);
5936707Sbrutus 
5946707Sbrutus cmplallocfail_addr_bind:
5956707Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
5966707Sbrutus cmplallocfail_mem_alloc:
5976707Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
5986707Sbrutus cmplallocfail_alloc_handle:
5996707Sbrutus 	return (DDI_FAILURE);
6006707Sbrutus }
6016707Sbrutus 
6026707Sbrutus 
6036707Sbrutus /*
6046707Sbrutus  * ioat_completion_free()
6056707Sbrutus  */
6066707Sbrutus void
ioat_completion_free(ioat_channel_t channel)6076707Sbrutus ioat_completion_free(ioat_channel_t channel)
6086707Sbrutus {
6096707Sbrutus 	ioat_state_t *state;
6106707Sbrutus 
6116707Sbrutus 	state = channel->ic_state;
6126707Sbrutus 
6136707Sbrutus 	/* reset the completion address register */
6146707Sbrutus 	ddi_put32(state->is_reg_handle,
6156707Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_LO], 0x0);
6166707Sbrutus 	ddi_put32(state->is_reg_handle,
6176707Sbrutus 	    (uint32_t *)&channel->ic_regs[IOAT_CHAN_CMPL_HI], 0x0);
6186707Sbrutus 
6196707Sbrutus 	/* unbind, then free up the memory, dma handle */
6206707Sbrutus 	(void) ddi_dma_unbind_handle(channel->ic_cmpl_dma_handle);
6216707Sbrutus 	ddi_dma_mem_free(&channel->ic_cmpl_handle);
6226707Sbrutus 	ddi_dma_free_handle(&channel->ic_cmpl_dma_handle);
6236707Sbrutus }
6246707Sbrutus 
6256707Sbrutus /*
6266707Sbrutus  * ioat_ring_alloc()
6276707Sbrutus  */
6286707Sbrutus int
ioat_ring_alloc(ioat_channel_t channel,uint_t desc_cnt)6296707Sbrutus ioat_ring_alloc(ioat_channel_t channel, uint_t desc_cnt)
6306707Sbrutus {
6316707Sbrutus 	ioat_channel_ring_t *ring;
6326707Sbrutus 	ioat_state_t *state;
6336707Sbrutus 	size_t real_length;
6346707Sbrutus 	uint_t cookie_cnt;
6356707Sbrutus 	int e;
6366707Sbrutus 
6376707Sbrutus 
6386707Sbrutus 	state = channel->ic_state;
6396707Sbrutus 
6406707Sbrutus 	ring = kmem_zalloc(sizeof (ioat_channel_ring_t), KM_SLEEP);
6416707Sbrutus 	channel->ic_ring = ring;
6426707Sbrutus 	ring->cr_chan = channel;
6436707Sbrutus 	ring->cr_post_cnt = 0;
6446707Sbrutus 
6456707Sbrutus 	mutex_init(&ring->cr_cmpl_mutex, NULL, MUTEX_DRIVER,
6466707Sbrutus 	    channel->ic_state->is_iblock_cookie);
6476707Sbrutus 	mutex_init(&ring->cr_desc_mutex, NULL, MUTEX_DRIVER,
6486707Sbrutus 	    channel->ic_state->is_iblock_cookie);
6496707Sbrutus 
6506707Sbrutus 	/*
6516707Sbrutus 	 * allocate memory for the ring, zero it out, and get the paddr.
6526707Sbrutus 	 * We'll allocate a physically contiguous chunck of memory  which
6536707Sbrutus 	 * simplifies the completion logic.
6546707Sbrutus 	 */
6556707Sbrutus 	e = ddi_dma_alloc_handle(state->is_dip, &ioat_desc_dma_attr,
6566707Sbrutus 	    DDI_DMA_SLEEP, NULL, &channel->ic_desc_dma_handle);
6576707Sbrutus 	if (e != DDI_SUCCESS) {
6586707Sbrutus 		goto ringallocfail_alloc_handle;
6596707Sbrutus 	}
6606707Sbrutus 	/*
6616707Sbrutus 	 * allocate one extra descriptor so we can simplify the empty/full
6626707Sbrutus 	 * logic. Then round that number up to a whole multiple of 4.
6636707Sbrutus 	 */
6646707Sbrutus 	channel->ic_chan_desc_cnt = ((desc_cnt + 1) + 3) & ~0x3;
6656707Sbrutus 	ring->cr_desc_last = channel->ic_chan_desc_cnt - 1;
6666707Sbrutus 	channel->ic_desc_alloc_size = channel->ic_chan_desc_cnt *
6676707Sbrutus 	    sizeof (ioat_chan_desc_t);
6686707Sbrutus 	e = ddi_dma_mem_alloc(channel->ic_desc_dma_handle,
6696707Sbrutus 	    channel->ic_desc_alloc_size, &ioat_acc_attr,
6706707Sbrutus 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
6716707Sbrutus 	    (caddr_t *)&ring->cr_desc, &real_length, &channel->ic_desc_handle);
6726707Sbrutus 	if (e != DDI_SUCCESS) {
6736707Sbrutus 		goto ringallocfail_mem_alloc;
6746707Sbrutus 	}
6756707Sbrutus 	bzero(ring->cr_desc, channel->ic_desc_alloc_size);
6766707Sbrutus 	e = ddi_dma_addr_bind_handle(channel->ic_desc_dma_handle, NULL,
6776707Sbrutus 	    (caddr_t)ring->cr_desc, channel->ic_desc_alloc_size,
6786707Sbrutus 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
6796707Sbrutus 	    &channel->ic_desc_cookies, &cookie_cnt);
6806707Sbrutus 	if (e != DDI_SUCCESS) {
6816707Sbrutus 		goto ringallocfail_addr_bind;
6826707Sbrutus 	}
6836707Sbrutus 	ASSERT(cookie_cnt == 1);
6846707Sbrutus 	ASSERT(channel->ic_desc_cookies.dmac_size ==
6856707Sbrutus 	    channel->ic_desc_alloc_size);
6866707Sbrutus 	ring->cr_phys_desc = channel->ic_desc_cookies.dmac_laddress;
6876707Sbrutus 
6886707Sbrutus 	/* write the physical address into the chain address register */
6896707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
6906707Sbrutus 		ddi_put32(state->is_reg_handle,
6916707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO],
6926707Sbrutus 		    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
6936707Sbrutus 		ddi_put32(state->is_reg_handle,
6946707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI],
6956707Sbrutus 		    (uint32_t)(ring->cr_phys_desc >> 32));
6966707Sbrutus 	} else {
6976707Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
6986707Sbrutus 		ddi_put32(state->is_reg_handle,
6996707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO],
7006707Sbrutus 		    (uint32_t)(ring->cr_phys_desc & 0xffffffff));
7016707Sbrutus 		ddi_put32(state->is_reg_handle,
7026707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI],
7036707Sbrutus 		    (uint32_t)(ring->cr_phys_desc >> 32));
7046707Sbrutus 	}
7056707Sbrutus 
7066707Sbrutus 	return (DCOPY_SUCCESS);
7076707Sbrutus 
7086707Sbrutus ringallocfail_addr_bind:
7096707Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
7106707Sbrutus ringallocfail_mem_alloc:
7116707Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
7126707Sbrutus ringallocfail_alloc_handle:
7136707Sbrutus 	mutex_destroy(&ring->cr_desc_mutex);
7146707Sbrutus 	mutex_destroy(&ring->cr_cmpl_mutex);
7156707Sbrutus 	kmem_free(channel->ic_ring, sizeof (ioat_channel_ring_t));
7166707Sbrutus 
7176707Sbrutus 	return (DCOPY_FAILURE);
7186707Sbrutus }
7196707Sbrutus 
7206707Sbrutus 
7216707Sbrutus /*
7226707Sbrutus  * ioat_ring_free()
7236707Sbrutus  */
7246707Sbrutus void
ioat_ring_free(ioat_channel_t channel)7256707Sbrutus ioat_ring_free(ioat_channel_t channel)
7266707Sbrutus {
7276707Sbrutus 	ioat_state_t *state;
7286707Sbrutus 
7296707Sbrutus 
7306707Sbrutus 	state = channel->ic_state;
7316707Sbrutus 
7326707Sbrutus 	/* reset the chain address register */
7336707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
7346707Sbrutus 		ddi_put32(state->is_reg_handle,
7356707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_LO], 0x0);
7366707Sbrutus 		ddi_put32(state->is_reg_handle,
7376707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V1_CHAN_ADDR_HI], 0x0);
7386707Sbrutus 	} else {
7396707Sbrutus 		ASSERT(channel->ic_ver == IOAT_CBv2);
7406707Sbrutus 		ddi_put32(state->is_reg_handle,
7416707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_LO], 0x0);
7426707Sbrutus 		ddi_put32(state->is_reg_handle,
7436707Sbrutus 		    (uint32_t *)&channel->ic_regs[IOAT_V2_CHAN_ADDR_HI], 0x0);
7446707Sbrutus 	}
7456707Sbrutus 
7466707Sbrutus 	/* unbind, then free up the memory, dma handle */
7476707Sbrutus 	(void) ddi_dma_unbind_handle(channel->ic_desc_dma_handle);
7486707Sbrutus 	ddi_dma_mem_free(&channel->ic_desc_handle);
7496707Sbrutus 	ddi_dma_free_handle(&channel->ic_desc_dma_handle);
7506707Sbrutus 
7516707Sbrutus 	mutex_destroy(&channel->ic_ring->cr_desc_mutex);
7526707Sbrutus 	mutex_destroy(&channel->ic_ring->cr_cmpl_mutex);
7536707Sbrutus 	kmem_free(channel->ic_ring, sizeof (ioat_channel_ring_t));
7546707Sbrutus 
7556707Sbrutus }
7566707Sbrutus 
7576707Sbrutus 
7586707Sbrutus /*
7596707Sbrutus  * ioat_ring_seed()
7606707Sbrutus  *    write the first descriptor in the ring.
7616707Sbrutus  */
7626707Sbrutus void
ioat_ring_seed(ioat_channel_t channel,ioat_chan_dma_desc_t * in_desc)7636707Sbrutus ioat_ring_seed(ioat_channel_t channel, ioat_chan_dma_desc_t *in_desc)
7646707Sbrutus {
7656707Sbrutus 	ioat_channel_ring_t *ring;
7666707Sbrutus 	ioat_chan_dma_desc_t *desc;
7676707Sbrutus 	ioat_chan_dma_desc_t *prev;
7686707Sbrutus 	ioat_state_t *state;
7696707Sbrutus 
7706707Sbrutus 
7716707Sbrutus 	state = channel->ic_state;
7726707Sbrutus 	ring = channel->ic_ring;
7736707Sbrutus 
7746707Sbrutus 	/* init the completion state */
7756707Sbrutus 	ring->cr_cmpl_gen = 0x0;
7766707Sbrutus 	ring->cr_cmpl_last = 0x0;
7776707Sbrutus 
7786707Sbrutus 	/* write in the descriptor and init the descriptor state */
7796707Sbrutus 	ring->cr_post_cnt++;
7806707Sbrutus 	channel->ic_ring->cr_desc[0] = *(ioat_chan_desc_t *)in_desc;
7816707Sbrutus 	ring->cr_desc_gen = 0;
7826707Sbrutus 	ring->cr_desc_prev = 0;
7836707Sbrutus 	ring->cr_desc_next = 1;
7846707Sbrutus 
7856707Sbrutus 	if (channel->ic_ver == IOAT_CBv1) {
7866707Sbrutus 		/* hit the start bit */
7876707Sbrutus 		ddi_put8(state->is_reg_handle,
7886707Sbrutus 		    &channel->ic_regs[IOAT_V1_CHAN_CMD], 0x1);
7896707Sbrutus 	} else {
7906707Sbrutus 		/*
7916707Sbrutus 		 * if this is CBv2, link the descriptor to an empty
7926707Sbrutus 		 * descriptor
7936707Sbrutus 		 */
7946707Sbrutus 		ASSERT(ring->cr_chan->ic_ver == IOAT_CBv2);
7956707Sbrutus 		desc = (ioat_chan_dma_desc_t *)
7966707Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
7976707Sbrutus 		prev = (ioat_chan_dma_desc_t *)
7986707Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
7996707Sbrutus 
8006707Sbrutus 		desc->dd_ctrl = 0;
8016707Sbrutus 		desc->dd_next_desc = 0x0;
8026707Sbrutus 
8036707Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
8046707Sbrutus 		    (ring->cr_desc_next << 6);
8056707Sbrutus 
8066707Sbrutus 		ddi_put16(state->is_reg_handle,
8076707Sbrutus 		    (uint16_t *)&channel->ic_regs[IOAT_V2_CHAN_CNT],
8086707Sbrutus 		    (uint16_t)1);
8096707Sbrutus 	}
8106707Sbrutus 
8116707Sbrutus }
8126707Sbrutus 
813*9638SRandy.Fishel@Sun.COM /*
814*9638SRandy.Fishel@Sun.COM  * ioat_ring_loop()
815*9638SRandy.Fishel@Sun.COM  * Make the ring loop for CB v1
816*9638SRandy.Fishel@Sun.COM  * This function assume we are in the ring->cr_desc_mutex mutex context
817*9638SRandy.Fishel@Sun.COM  */
818*9638SRandy.Fishel@Sun.COM int
ioat_ring_loop(ioat_channel_ring_t * ring,dcopy_cmd_t cmd)819*9638SRandy.Fishel@Sun.COM ioat_ring_loop(ioat_channel_ring_t *ring, dcopy_cmd_t cmd)
820*9638SRandy.Fishel@Sun.COM {
821*9638SRandy.Fishel@Sun.COM 	uint64_t count;
822*9638SRandy.Fishel@Sun.COM 	ioat_channel_t channel;
823*9638SRandy.Fishel@Sun.COM 	ioat_chan_dma_desc_t *curr;
824*9638SRandy.Fishel@Sun.COM 	ioat_cmd_private_t *prevpriv;
825*9638SRandy.Fishel@Sun.COM 	ioat_cmd_private_t *currpriv;
826*9638SRandy.Fishel@Sun.COM 
827*9638SRandy.Fishel@Sun.COM 	channel = ring->cr_chan;
828*9638SRandy.Fishel@Sun.COM 	ASSERT(channel->ic_ver == IOAT_CBv1);
829*9638SRandy.Fishel@Sun.COM 
830*9638SRandy.Fishel@Sun.COM 	/*
831*9638SRandy.Fishel@Sun.COM 	 * For each cmd in the command queue, check whether they are continuous
832*9638SRandy.Fishel@Sun.COM 	 * in descriptor ring. Return error if not continuous.
833*9638SRandy.Fishel@Sun.COM 	 */
834*9638SRandy.Fishel@Sun.COM 	for (count = 0, prevpriv = NULL;
835*9638SRandy.Fishel@Sun.COM 	    cmd != NULL && count <= channel->ic_chan_desc_cnt;
836*9638SRandy.Fishel@Sun.COM 	    prevpriv = currpriv) {
837*9638SRandy.Fishel@Sun.COM 		currpriv = cmd->dp_private->pr_device_cmd_private;
838*9638SRandy.Fishel@Sun.COM 		if (prevpriv != NULL &&
839*9638SRandy.Fishel@Sun.COM 		    currpriv->ip_index + 1 != prevpriv->ip_start &&
840*9638SRandy.Fishel@Sun.COM 		    currpriv->ip_index + 1 != prevpriv->ip_start +
841*9638SRandy.Fishel@Sun.COM 		    channel->ic_chan_desc_cnt) {
842*9638SRandy.Fishel@Sun.COM 			/* Non-continuous, other commands get interleaved */
843*9638SRandy.Fishel@Sun.COM 			return (DCOPY_FAILURE);
844*9638SRandy.Fishel@Sun.COM 		}
845*9638SRandy.Fishel@Sun.COM 		if (currpriv->ip_index < currpriv->ip_start) {
846*9638SRandy.Fishel@Sun.COM 			count += channel->ic_chan_desc_cnt
847*9638SRandy.Fishel@Sun.COM 			    + currpriv->ip_index - currpriv->ip_start + 1;
848*9638SRandy.Fishel@Sun.COM 		} else {
849*9638SRandy.Fishel@Sun.COM 			count += currpriv->ip_index - currpriv->ip_start + 1;
850*9638SRandy.Fishel@Sun.COM 		}
851*9638SRandy.Fishel@Sun.COM 		cmd = currpriv->ip_next;
852*9638SRandy.Fishel@Sun.COM 	}
853*9638SRandy.Fishel@Sun.COM 	/*
854*9638SRandy.Fishel@Sun.COM 	 * Check for too many descriptors which would cause wrap around in
855*9638SRandy.Fishel@Sun.COM 	 * descriptor ring. And make sure there is space for cancel operation.
856*9638SRandy.Fishel@Sun.COM 	 */
857*9638SRandy.Fishel@Sun.COM 	if (count >= channel->ic_chan_desc_cnt) {
858*9638SRandy.Fishel@Sun.COM 		return (DCOPY_FAILURE);
859*9638SRandy.Fishel@Sun.COM 	}
860*9638SRandy.Fishel@Sun.COM 
861*9638SRandy.Fishel@Sun.COM 	/* Point next descriptor to header of chain. */
862*9638SRandy.Fishel@Sun.COM 	curr = (ioat_chan_dma_desc_t *)&ring->cr_desc[ring->cr_desc_prev];
863*9638SRandy.Fishel@Sun.COM 	curr->dd_next_desc = ring->cr_phys_desc + (currpriv->ip_start << 6);
864*9638SRandy.Fishel@Sun.COM 
865*9638SRandy.Fishel@Sun.COM 	/* sync the last desc */
866*9638SRandy.Fishel@Sun.COM 	(void) ddi_dma_sync(channel->ic_desc_dma_handle,
867*9638SRandy.Fishel@Sun.COM 	    ring->cr_desc_prev << 6, 64, DDI_DMA_SYNC_FORDEV);
868*9638SRandy.Fishel@Sun.COM 
869*9638SRandy.Fishel@Sun.COM 	return (DCOPY_SUCCESS);
870*9638SRandy.Fishel@Sun.COM }
871*9638SRandy.Fishel@Sun.COM 
8726707Sbrutus 
8736707Sbrutus /*
8746707Sbrutus  * ioat_cmd_alloc()
8756707Sbrutus  */
8766707Sbrutus int
ioat_cmd_alloc(void * private,int flags,dcopy_cmd_t * cmd)8776707Sbrutus ioat_cmd_alloc(void *private, int flags, dcopy_cmd_t *cmd)
8786707Sbrutus {
8796707Sbrutus 	ioat_cmd_private_t *priv;
8806707Sbrutus 	ioat_channel_t channel;
8816707Sbrutus 	dcopy_cmd_t oldcmd;
8826707Sbrutus 	int kmflag;
8836707Sbrutus 
8846707Sbrutus 
8856707Sbrutus 	channel = (ioat_channel_t)private;
8866707Sbrutus 
8876707Sbrutus 	if (flags & DCOPY_NOSLEEP) {
8886707Sbrutus 		kmflag = KM_NOSLEEP;
8896707Sbrutus 	} else {
8906707Sbrutus 		kmflag = KM_SLEEP;
8916707Sbrutus 	}
8926707Sbrutus 
8936707Sbrutus 	/* save the command passed incase DCOPY_ALLOC_LINK is set */
8946707Sbrutus 	oldcmd = *cmd;
8956707Sbrutus 
8966707Sbrutus 	*cmd = kmem_cache_alloc(channel->ic_cmd_cache, kmflag);
8976707Sbrutus 	if (*cmd == NULL) {
8986707Sbrutus 		return (DCOPY_NORESOURCES);
8996707Sbrutus 	}
9006707Sbrutus 
9016707Sbrutus 	/* setup the dcopy and ioat private state pointers */
9026707Sbrutus 	(*cmd)->dp_version = DCOPY_CMD_V0;
9036707Sbrutus 	(*cmd)->dp_cmd = 0;
9046707Sbrutus 	(*cmd)->dp_private = (struct dcopy_cmd_priv_s *)
9056707Sbrutus 	    ((uintptr_t)(*cmd) + sizeof (struct dcopy_cmd_s));
9066707Sbrutus 	(*cmd)->dp_private->pr_device_cmd_private =
9076707Sbrutus 	    (struct ioat_cmd_private_s *)((uintptr_t)(*cmd)->dp_private +
9086707Sbrutus 	    sizeof (struct dcopy_cmd_priv_s));
9096707Sbrutus 
9106707Sbrutus 	/*
9116707Sbrutus 	 * if DCOPY_ALLOC_LINK is set, link the old command to the new one
9126707Sbrutus 	 * just allocated.
9136707Sbrutus 	 */
9146707Sbrutus 	priv = (*cmd)->dp_private->pr_device_cmd_private;
9156707Sbrutus 	if (flags & DCOPY_ALLOC_LINK) {
9166707Sbrutus 		priv->ip_next = oldcmd;
9176707Sbrutus 	} else {
9186707Sbrutus 		priv->ip_next = NULL;
9196707Sbrutus 	}
9206707Sbrutus 
9216707Sbrutus 	return (DCOPY_SUCCESS);
9226707Sbrutus }
9236707Sbrutus 
9246707Sbrutus 
9256707Sbrutus /*
9266707Sbrutus  * ioat_cmd_free()
9276707Sbrutus  */
9286707Sbrutus void
ioat_cmd_free(void * private,dcopy_cmd_t * cmdp)9296707Sbrutus ioat_cmd_free(void *private, dcopy_cmd_t *cmdp)
9306707Sbrutus {
9316707Sbrutus 	ioat_cmd_private_t *priv;
9326707Sbrutus 	ioat_channel_t channel;
9336707Sbrutus 	dcopy_cmd_t next;
9346707Sbrutus 	dcopy_cmd_t cmd;
9356707Sbrutus 
9366707Sbrutus 
9376707Sbrutus 	channel = (ioat_channel_t)private;
9386707Sbrutus 	cmd = *(cmdp);
9396707Sbrutus 
9406707Sbrutus 	/*
9416707Sbrutus 	 * free all the commands in the chain (see DCOPY_ALLOC_LINK in
9426707Sbrutus 	 * ioat_cmd_alloc() for more info).
9436707Sbrutus 	 */
9446707Sbrutus 	while (cmd != NULL) {
9456707Sbrutus 		priv = cmd->dp_private->pr_device_cmd_private;
9466707Sbrutus 		next = priv->ip_next;
9476707Sbrutus 		kmem_cache_free(channel->ic_cmd_cache, cmd);
9486707Sbrutus 		cmd = next;
9496707Sbrutus 	}
9506707Sbrutus 	*cmdp = NULL;
9516707Sbrutus }
9526707Sbrutus 
9536707Sbrutus 
9546707Sbrutus /*
9556707Sbrutus  * ioat_cmd_post()
9566707Sbrutus  */
9576707Sbrutus int
ioat_cmd_post(void * private,dcopy_cmd_t cmd)9586707Sbrutus ioat_cmd_post(void *private, dcopy_cmd_t cmd)
9596707Sbrutus {
9606707Sbrutus 	ioat_channel_ring_t *ring;
9616707Sbrutus 	ioat_cmd_private_t *priv;
9626707Sbrutus 	ioat_channel_t channel;
9636707Sbrutus 	ioat_state_t *state;
9646707Sbrutus 	uint64_t dest_paddr;
9656707Sbrutus 	uint64_t src_paddr;
9666707Sbrutus 	uint64_t dest_addr;
9676707Sbrutus 	uint32_t dest_size;
9686707Sbrutus 	uint64_t src_addr;
9696707Sbrutus 	uint32_t src_size;
9706707Sbrutus 	size_t xfer_size;
9716707Sbrutus 	uint32_t ctrl;
9726707Sbrutus 	size_t size;
9736707Sbrutus 	int e;
9746707Sbrutus 
9756707Sbrutus 
9766707Sbrutus 	channel = (ioat_channel_t)private;
9776707Sbrutus 	priv = cmd->dp_private->pr_device_cmd_private;
9786707Sbrutus 
9796707Sbrutus 	state = channel->ic_state;
9806707Sbrutus 	ring = channel->ic_ring;
9816707Sbrutus 
982*9638SRandy.Fishel@Sun.COM 	/*
983*9638SRandy.Fishel@Sun.COM 	 * Special support for DCOPY_CMD_LOOP option, only supported on CBv1.
984*9638SRandy.Fishel@Sun.COM 	 * DCOPY_CMD_QUEUE should also be set if DCOPY_CMD_LOOP is set.
985*9638SRandy.Fishel@Sun.COM 	 */
986*9638SRandy.Fishel@Sun.COM 	if ((cmd->dp_flags & DCOPY_CMD_LOOP) &&
987*9638SRandy.Fishel@Sun.COM 	    (channel->ic_ver != IOAT_CBv1 ||
988*9638SRandy.Fishel@Sun.COM 	    (cmd->dp_flags & DCOPY_CMD_QUEUE))) {
989*9638SRandy.Fishel@Sun.COM 		return (DCOPY_FAILURE);
990*9638SRandy.Fishel@Sun.COM 	}
991*9638SRandy.Fishel@Sun.COM 
992*9638SRandy.Fishel@Sun.COM 	if ((cmd->dp_flags & DCOPY_CMD_NOWAIT) == 0) {
993*9638SRandy.Fishel@Sun.COM 		mutex_enter(&ring->cr_desc_mutex);
994*9638SRandy.Fishel@Sun.COM 
995*9638SRandy.Fishel@Sun.COM 	/*
996*9638SRandy.Fishel@Sun.COM 	 * Try to acquire mutex if NOWAIT flag is set.
997*9638SRandy.Fishel@Sun.COM 	 * Return failure if failed to acquire mutex.
998*9638SRandy.Fishel@Sun.COM 	 */
999*9638SRandy.Fishel@Sun.COM 	} else if (mutex_tryenter(&ring->cr_desc_mutex) == 0) {
1000*9638SRandy.Fishel@Sun.COM 		return (DCOPY_FAILURE);
1001*9638SRandy.Fishel@Sun.COM 	}
10026707Sbrutus 
10036707Sbrutus 	/* if the channel has had a fatal failure, return failure */
10046707Sbrutus 	if (channel->ic_channel_state == IOAT_CHANNEL_IN_FAILURE) {
10059064SMark.Johnson@Sun.COM 		mutex_exit(&ring->cr_desc_mutex);
10066707Sbrutus 		return (DCOPY_FAILURE);
10076707Sbrutus 	}
10086707Sbrutus 
10096707Sbrutus 	/* make sure we have space for the descriptors */
10106707Sbrutus 	e = ioat_ring_reserve(channel, ring, cmd);
10116707Sbrutus 	if (e != DCOPY_SUCCESS) {
10129064SMark.Johnson@Sun.COM 		mutex_exit(&ring->cr_desc_mutex);
10136707Sbrutus 		return (DCOPY_NORESOURCES);
10146707Sbrutus 	}
10156707Sbrutus 
10166707Sbrutus 	/* if we support DCA, and the DCA flag is set, post a DCA desc */
10176707Sbrutus 	if ((channel->ic_ver == IOAT_CBv2) &&
10186707Sbrutus 	    (cmd->dp_flags & DCOPY_CMD_DCA)) {
10196707Sbrutus 		ioat_cmd_post_dca(ring, cmd->dp_dca_id);
10206707Sbrutus 	}
10216707Sbrutus 
10226707Sbrutus 	/*
10236707Sbrutus 	 * the dma copy may have to be broken up into multiple descriptors
10246707Sbrutus 	 * since we can't cross a page boundary.
10256707Sbrutus 	 */
10266707Sbrutus 	ASSERT(cmd->dp_version == DCOPY_CMD_V0);
10276707Sbrutus 	ASSERT(cmd->dp_cmd == DCOPY_CMD_COPY);
10286707Sbrutus 	src_addr = cmd->dp.copy.cc_source;
10296707Sbrutus 	dest_addr = cmd->dp.copy.cc_dest;
10306707Sbrutus 	size = cmd->dp.copy.cc_size;
1031*9638SRandy.Fishel@Sun.COM 	priv->ip_start = ring->cr_desc_next;
10326707Sbrutus 	while (size > 0) {
10336707Sbrutus 		src_paddr = pa_to_ma(src_addr);
10346707Sbrutus 		dest_paddr = pa_to_ma(dest_addr);
10356707Sbrutus 
10366707Sbrutus 		/* adjust for any offset into the page */
10376707Sbrutus 		if ((src_addr & PAGEOFFSET) == 0) {
10386707Sbrutus 			src_size = PAGESIZE;
10396707Sbrutus 		} else {
10406707Sbrutus 			src_size = PAGESIZE - (src_addr & PAGEOFFSET);
10416707Sbrutus 		}
10426707Sbrutus 		if ((dest_addr & PAGEOFFSET) == 0) {
10436707Sbrutus 			dest_size = PAGESIZE;
10446707Sbrutus 		} else {
10456707Sbrutus 			dest_size = PAGESIZE - (dest_addr & PAGEOFFSET);
10466707Sbrutus 		}
10476707Sbrutus 
10486707Sbrutus 		/* take the smallest of the three */
10496707Sbrutus 		xfer_size = MIN(src_size, dest_size);
10506707Sbrutus 		xfer_size = MIN(xfer_size, size);
10516707Sbrutus 
10526707Sbrutus 		/*
10536707Sbrutus 		 * if this is the last descriptor, and we are supposed to
10546707Sbrutus 		 * generate a completion, generate a completion. same logic
10556707Sbrutus 		 * for interrupt.
10566707Sbrutus 		 */
10576707Sbrutus 		ctrl = 0;
1058*9638SRandy.Fishel@Sun.COM 		if (cmd->dp_flags & DCOPY_CMD_NOSRCSNP) {
1059*9638SRandy.Fishel@Sun.COM 			ctrl |= IOAT_DESC_CTRL_NOSRCSNP;
1060*9638SRandy.Fishel@Sun.COM 		}
1061*9638SRandy.Fishel@Sun.COM 		if (cmd->dp_flags & DCOPY_CMD_NODSTSNP) {
1062*9638SRandy.Fishel@Sun.COM 			ctrl |= IOAT_DESC_CTRL_NODSTSNP;
1063*9638SRandy.Fishel@Sun.COM 		}
10646707Sbrutus 		if (xfer_size == size) {
10656707Sbrutus 			if (!(cmd->dp_flags & DCOPY_CMD_NOSTAT)) {
10666707Sbrutus 				ctrl |= IOAT_DESC_CTRL_CMPL;
10676707Sbrutus 			}
10686707Sbrutus 			if ((cmd->dp_flags & DCOPY_CMD_INTR)) {
10696707Sbrutus 				ctrl |= IOAT_DESC_CTRL_INTR;
10706707Sbrutus 			}
10716707Sbrutus 		}
10726707Sbrutus 
10736707Sbrutus 		ioat_cmd_post_copy(ring, src_paddr, dest_paddr, xfer_size,
10746707Sbrutus 		    ctrl);
10756707Sbrutus 
10766707Sbrutus 		/* go to the next page */
10776707Sbrutus 		src_addr += xfer_size;
10786707Sbrutus 		dest_addr += xfer_size;
10796707Sbrutus 		size -= xfer_size;
10806707Sbrutus 	}
10816707Sbrutus 
1082*9638SRandy.Fishel@Sun.COM 	/* save away the state so we can poll on it. */
1083*9638SRandy.Fishel@Sun.COM 	priv->ip_generation = ring->cr_desc_gen_prev;
1084*9638SRandy.Fishel@Sun.COM 	priv->ip_index = ring->cr_desc_prev;
10856707Sbrutus 
10866707Sbrutus 	/* if queue not defined, tell the DMA engine about it */
10876707Sbrutus 	if (!(cmd->dp_flags & DCOPY_CMD_QUEUE)) {
1088*9638SRandy.Fishel@Sun.COM 		/*
1089*9638SRandy.Fishel@Sun.COM 		 * Link the ring to a loop (currently only for FIPE).
1090*9638SRandy.Fishel@Sun.COM 		 */
1091*9638SRandy.Fishel@Sun.COM 		if (cmd->dp_flags & DCOPY_CMD_LOOP) {
1092*9638SRandy.Fishel@Sun.COM 			e = ioat_ring_loop(ring, cmd);
1093*9638SRandy.Fishel@Sun.COM 			if (e != DCOPY_SUCCESS) {
1094*9638SRandy.Fishel@Sun.COM 				mutex_exit(&ring->cr_desc_mutex);
1095*9638SRandy.Fishel@Sun.COM 				return (DCOPY_FAILURE);
1096*9638SRandy.Fishel@Sun.COM 			}
1097*9638SRandy.Fishel@Sun.COM 		}
1098*9638SRandy.Fishel@Sun.COM 
10996707Sbrutus 		if (channel->ic_ver == IOAT_CBv1) {
11006707Sbrutus 			ddi_put8(state->is_reg_handle,
11016707Sbrutus 			    (uint8_t *)&channel->ic_regs[IOAT_V1_CHAN_CMD],
11026707Sbrutus 			    0x2);
11036707Sbrutus 		} else {
11046707Sbrutus 			ASSERT(channel->ic_ver == IOAT_CBv2);
11056707Sbrutus 			ddi_put16(state->is_reg_handle,
11066707Sbrutus 			    (uint16_t *)&channel->ic_regs[IOAT_V2_CHAN_CNT],
11076707Sbrutus 			    (uint16_t)(ring->cr_post_cnt & 0xFFFF));
11086707Sbrutus 		}
11096707Sbrutus 	}
11106707Sbrutus 
11116707Sbrutus 	mutex_exit(&ring->cr_desc_mutex);
11126707Sbrutus 
11136707Sbrutus 	return (DCOPY_SUCCESS);
11146707Sbrutus }
11156707Sbrutus 
11166707Sbrutus 
11176707Sbrutus /*
11186707Sbrutus  * ioat_cmd_post_dca()
11196707Sbrutus  */
11206707Sbrutus static void
ioat_cmd_post_dca(ioat_channel_ring_t * ring,uint32_t dca_id)11216707Sbrutus ioat_cmd_post_dca(ioat_channel_ring_t *ring, uint32_t dca_id)
11226707Sbrutus {
11239628SMark.Johnson@Sun.COM 	ioat_chan_dca_desc_t *saved_prev;
11246707Sbrutus 	ioat_chan_dca_desc_t *desc;
11256707Sbrutus 	ioat_chan_dca_desc_t *prev;
11266707Sbrutus 	ioat_channel_t channel;
11279628SMark.Johnson@Sun.COM 	uint64_t next_desc_phys;
11289628SMark.Johnson@Sun.COM 	off_t prev_offset;
11299628SMark.Johnson@Sun.COM 	off_t next_offset;
11306707Sbrutus 
11316707Sbrutus 
11326707Sbrutus 	channel = ring->cr_chan;
11336707Sbrutus 	desc = (ioat_chan_dca_desc_t *)&ring->cr_desc[ring->cr_desc_next];
11346707Sbrutus 	prev = (ioat_chan_dca_desc_t *)&ring->cr_desc[ring->cr_desc_prev];
11356707Sbrutus 
11366707Sbrutus 	/* keep track of the number of descs posted for cbv2 */
11376707Sbrutus 	ring->cr_post_cnt++;
11386707Sbrutus 
11396707Sbrutus 	/*
11406707Sbrutus 	 * post a context change desriptor. If dca has never been used on
11416707Sbrutus 	 * this channel, or if the id doesn't match the last id used on this
11426707Sbrutus 	 * channel, set CONTEXT_CHANGE bit and dca id, set dca state to active,
11436707Sbrutus 	 * and save away the id we're using.
11446707Sbrutus 	 */
11456707Sbrutus 	desc->dd_ctrl = IOAT_DESC_CTRL_OP_CNTX;
11466707Sbrutus 	desc->dd_next_desc = 0x0;
11476707Sbrutus 	if (!channel->ic_dca_active || (channel->ic_dca_current != dca_id)) {
11486707Sbrutus 		channel->ic_dca_active = B_TRUE;
11496707Sbrutus 		channel->ic_dca_current = dca_id;
11506707Sbrutus 		desc->dd_ctrl |= IOAT_DESC_CTRL_CNTX_CHNG;
11516707Sbrutus 		desc->dd_cntx = dca_id;
11526707Sbrutus 	}
11536707Sbrutus 
11549628SMark.Johnson@Sun.COM 	/*
11559628SMark.Johnson@Sun.COM 	 * save next desc and prev offset for when we link the two
11569628SMark.Johnson@Sun.COM 	 * descriptors together.
11579628SMark.Johnson@Sun.COM 	 */
11589628SMark.Johnson@Sun.COM 	saved_prev = prev;
11599628SMark.Johnson@Sun.COM 	prev_offset = ring->cr_desc_prev << 6;
11609628SMark.Johnson@Sun.COM 	next_offset = ring->cr_desc_next << 6;
11619628SMark.Johnson@Sun.COM 	next_desc_phys = ring->cr_phys_desc + next_offset;
11626707Sbrutus 
11636707Sbrutus 	/* save the current desc_next and desc_last for the completion */
11646707Sbrutus 	ring->cr_desc_prev = ring->cr_desc_next;
11656707Sbrutus 	ring->cr_desc_gen_prev = ring->cr_desc_gen;
11666707Sbrutus 
11676707Sbrutus 	/* increment next/gen so it points to the next free desc */
11686707Sbrutus 	ring->cr_desc_next++;
11696707Sbrutus 	if (ring->cr_desc_next > ring->cr_desc_last) {
11706707Sbrutus 		ring->cr_desc_next = 0;
11716707Sbrutus 		ring->cr_desc_gen++;
11726707Sbrutus 	}
11736707Sbrutus 
11746707Sbrutus 	/*
11756707Sbrutus 	 * if this is CBv2, link the descriptor to an empty descriptor. Since
11766707Sbrutus 	 * we always leave on desc empty to detect full, this works out.
11776707Sbrutus 	 */
11786707Sbrutus 	if (ring->cr_chan->ic_ver == IOAT_CBv2) {
11796707Sbrutus 		desc = (ioat_chan_dca_desc_t *)
11806707Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
11816707Sbrutus 		prev = (ioat_chan_dca_desc_t *)
11826707Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
11836707Sbrutus 		desc->dd_ctrl = 0;
11846707Sbrutus 		desc->dd_next_desc = 0x0;
11859628SMark.Johnson@Sun.COM 		(void) ddi_dma_sync(channel->ic_desc_dma_handle,
11869628SMark.Johnson@Sun.COM 		    ring->cr_desc_next << 6, 64, DDI_DMA_SYNC_FORDEV);
11876707Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
11886707Sbrutus 		    (ring->cr_desc_next << 6);
11896707Sbrutus 	}
11909628SMark.Johnson@Sun.COM 
11919628SMark.Johnson@Sun.COM 	/* Put the descriptors physical address in the previous descriptor */
11929628SMark.Johnson@Sun.COM 	/*LINTED:E_TRUE_LOGICAL_EXPR*/
11939628SMark.Johnson@Sun.COM 	ASSERT(sizeof (ioat_chan_dca_desc_t) == 64);
11949628SMark.Johnson@Sun.COM 
11959628SMark.Johnson@Sun.COM 	/* sync the current desc */
11969628SMark.Johnson@Sun.COM 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, next_offset, 64,
11979628SMark.Johnson@Sun.COM 	    DDI_DMA_SYNC_FORDEV);
11989628SMark.Johnson@Sun.COM 
11999628SMark.Johnson@Sun.COM 	/* update the previous desc and sync it too */
12009628SMark.Johnson@Sun.COM 	saved_prev->dd_next_desc = next_desc_phys;
12019628SMark.Johnson@Sun.COM 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, prev_offset, 64,
12029628SMark.Johnson@Sun.COM 	    DDI_DMA_SYNC_FORDEV);
12036707Sbrutus }
12046707Sbrutus 
12056707Sbrutus 
12066707Sbrutus /*
12076707Sbrutus  * ioat_cmd_post_copy()
12086707Sbrutus  *
12096707Sbrutus  */
12106707Sbrutus 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)12116707Sbrutus ioat_cmd_post_copy(ioat_channel_ring_t *ring, uint64_t src_addr,
12126707Sbrutus     uint64_t dest_addr, uint32_t size, uint32_t ctrl)
12136707Sbrutus {
12149628SMark.Johnson@Sun.COM 	ioat_chan_dma_desc_t *saved_prev;
12156707Sbrutus 	ioat_chan_dma_desc_t *desc;
12166707Sbrutus 	ioat_chan_dma_desc_t *prev;
12176707Sbrutus 	ioat_channel_t channel;
12189628SMark.Johnson@Sun.COM 	uint64_t next_desc_phy;
12199628SMark.Johnson@Sun.COM 	off_t prev_offset;
12209628SMark.Johnson@Sun.COM 	off_t next_offset;
12216707Sbrutus 
12226707Sbrutus 
12236707Sbrutus 	channel = ring->cr_chan;
12246707Sbrutus 	desc = (ioat_chan_dma_desc_t *)&ring->cr_desc[ring->cr_desc_next];
12256707Sbrutus 	prev = (ioat_chan_dma_desc_t *)&ring->cr_desc[ring->cr_desc_prev];
12266707Sbrutus 
12276707Sbrutus 	/* keep track of the number of descs posted for cbv2 */
12286707Sbrutus 	ring->cr_post_cnt++;
12296707Sbrutus 
12306707Sbrutus 	/* write in the DMA desc */
12316707Sbrutus 	desc->dd_ctrl = IOAT_DESC_CTRL_OP_DMA | ctrl;
12326707Sbrutus 	desc->dd_size = size;
12336707Sbrutus 	desc->dd_src_paddr = src_addr;
12346707Sbrutus 	desc->dd_dest_paddr = dest_addr;
12356707Sbrutus 	desc->dd_next_desc = 0x0;
12366707Sbrutus 
12379628SMark.Johnson@Sun.COM 	/*
12389628SMark.Johnson@Sun.COM 	 * save next desc and prev offset for when we link the two
12399628SMark.Johnson@Sun.COM 	 * descriptors together.
12409628SMark.Johnson@Sun.COM 	 */
12419628SMark.Johnson@Sun.COM 	saved_prev = prev;
12429628SMark.Johnson@Sun.COM 	prev_offset = ring->cr_desc_prev << 6;
12439628SMark.Johnson@Sun.COM 	next_offset = ring->cr_desc_next << 6;
12449628SMark.Johnson@Sun.COM 	next_desc_phy = ring->cr_phys_desc + next_offset;
12456707Sbrutus 
12466707Sbrutus 	/* increment next/gen so it points to the next free desc */
12476707Sbrutus 	ring->cr_desc_prev = ring->cr_desc_next;
12486707Sbrutus 	ring->cr_desc_gen_prev = ring->cr_desc_gen;
12496707Sbrutus 
12506707Sbrutus 	/* increment next/gen so it points to the next free desc */
12516707Sbrutus 	ring->cr_desc_next++;
12526707Sbrutus 	if (ring->cr_desc_next > ring->cr_desc_last) {
12536707Sbrutus 		ring->cr_desc_next = 0;
12546707Sbrutus 		ring->cr_desc_gen++;
12556707Sbrutus 	}
12566707Sbrutus 
12576707Sbrutus 	/*
12586707Sbrutus 	 * if this is CBv2, link the descriptor to an empty descriptor. Since
12596707Sbrutus 	 * we always leave on desc empty to detect full, this works out.
12606707Sbrutus 	 */
12616707Sbrutus 	if (ring->cr_chan->ic_ver == IOAT_CBv2) {
12626707Sbrutus 		desc = (ioat_chan_dma_desc_t *)
12636707Sbrutus 		    &ring->cr_desc[ring->cr_desc_next];
12646707Sbrutus 		prev = (ioat_chan_dma_desc_t *)
12656707Sbrutus 		    &ring->cr_desc[ring->cr_desc_prev];
12666707Sbrutus 		desc->dd_size = 0;
12676707Sbrutus 		desc->dd_ctrl = 0;
12686707Sbrutus 		desc->dd_next_desc = 0x0;
12699628SMark.Johnson@Sun.COM 		(void) ddi_dma_sync(channel->ic_desc_dma_handle,
12709628SMark.Johnson@Sun.COM 		    ring->cr_desc_next << 6, 64, DDI_DMA_SYNC_FORDEV);
12716707Sbrutus 		prev->dd_next_desc = ring->cr_phys_desc +
12726707Sbrutus 		    (ring->cr_desc_next << 6);
12736707Sbrutus 	}
12749628SMark.Johnson@Sun.COM 
12759628SMark.Johnson@Sun.COM 	/* Put the descriptors physical address in the previous descriptor */
12769628SMark.Johnson@Sun.COM 	/*LINTED:E_TRUE_LOGICAL_EXPR*/
12779628SMark.Johnson@Sun.COM 	ASSERT(sizeof (ioat_chan_dma_desc_t) == 64);
12789628SMark.Johnson@Sun.COM 
12799628SMark.Johnson@Sun.COM 	/* sync the current desc */
12809628SMark.Johnson@Sun.COM 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, next_offset, 64,
12819628SMark.Johnson@Sun.COM 	    DDI_DMA_SYNC_FORDEV);
12829628SMark.Johnson@Sun.COM 
12839628SMark.Johnson@Sun.COM 	/* update the previous desc and sync it too */
12849628SMark.Johnson@Sun.COM 	saved_prev->dd_next_desc = next_desc_phy;
12859628SMark.Johnson@Sun.COM 	(void) ddi_dma_sync(channel->ic_desc_dma_handle, prev_offset, 64,
12869628SMark.Johnson@Sun.COM 	    DDI_DMA_SYNC_FORDEV);
12876707Sbrutus }
12886707Sbrutus 
12896707Sbrutus 
12906707Sbrutus /*
12916707Sbrutus  * ioat_cmd_poll()
12926707Sbrutus  */
12936707Sbrutus int
ioat_cmd_poll(void * private,dcopy_cmd_t cmd)12946707Sbrutus ioat_cmd_poll(void *private, dcopy_cmd_t cmd)
12956707Sbrutus {
12966707Sbrutus 	ioat_channel_ring_t *ring;
12976707Sbrutus 	ioat_cmd_private_t *priv;
12986707Sbrutus 	ioat_channel_t channel;
12996707Sbrutus 	uint64_t generation;
13006707Sbrutus 	uint64_t last_cmpl;
13016707Sbrutus 
1302*9638SRandy.Fishel@Sun.COM 	ASSERT(cmd != NULL);
13036707Sbrutus 	channel = (ioat_channel_t)private;
13046707Sbrutus 	priv = cmd->dp_private->pr_device_cmd_private;
13056707Sbrutus 
13066707Sbrutus 	ring = channel->ic_ring;
13076707Sbrutus 	ASSERT(ring != NULL);
13086707Sbrutus 
1309*9638SRandy.Fishel@Sun.COM 	if ((cmd->dp_flags & DCOPY_CMD_NOWAIT) == 0) {
1310*9638SRandy.Fishel@Sun.COM 		mutex_enter(&ring->cr_cmpl_mutex);
1311*9638SRandy.Fishel@Sun.COM 
1312*9638SRandy.Fishel@Sun.COM 	/*
1313*9638SRandy.Fishel@Sun.COM 	 * Try to acquire mutex if NOWAIT flag is set.
1314*9638SRandy.Fishel@Sun.COM 	 * Return failure if failed to acquire mutex.
1315*9638SRandy.Fishel@Sun.COM 	 */
1316*9638SRandy.Fishel@Sun.COM 	} else if (mutex_tryenter(&ring->cr_cmpl_mutex) == 0) {
1317*9638SRandy.Fishel@Sun.COM 		return (DCOPY_FAILURE);
1318*9638SRandy.Fishel@Sun.COM 	}
13196707Sbrutus 
13206707Sbrutus 	/* if the channel had a fatal failure, fail all polls */
13216707Sbrutus 	if ((channel->ic_channel_state == IOAT_CHANNEL_IN_FAILURE) ||
13226707Sbrutus 	    IOAT_CMPL_FAILED(channel)) {
13236707Sbrutus 		mutex_exit(&ring->cr_cmpl_mutex);
13246707Sbrutus 		return (DCOPY_FAILURE);
13256707Sbrutus 	}
13266707Sbrutus 
13276707Sbrutus 	/*
13286707Sbrutus 	 * if the current completion is the same as the last time we read one,
13296707Sbrutus 	 * post is still pending, nothing further to do. We track completions
13306707Sbrutus 	 * as indexes into the ring since post uses VAs and the H/W returns
13316707Sbrutus 	 * PAs. We grab a snapshot of generation and last_cmpl in the mutex.
13326707Sbrutus 	 */
13336707Sbrutus 	(void) ddi_dma_sync(channel->ic_cmpl_dma_handle, 0, 0,
13346707Sbrutus 	    DDI_DMA_SYNC_FORCPU);
13356707Sbrutus 	last_cmpl = IOAT_CMPL_INDEX(channel);
13366707Sbrutus 	if (last_cmpl != ring->cr_cmpl_last) {
13376707Sbrutus 		/*
13386707Sbrutus 		 * if we wrapped the ring, increment the generation. Store
13396707Sbrutus 		 * the last cmpl. This logic assumes a physically contiguous
13406707Sbrutus 		 * ring.
13416707Sbrutus 		 */
13426707Sbrutus 		if (last_cmpl < ring->cr_cmpl_last) {
13436707Sbrutus 			ring->cr_cmpl_gen++;
13446707Sbrutus 		}
13456707Sbrutus 		ring->cr_cmpl_last = last_cmpl;
13466707Sbrutus 		generation = ring->cr_cmpl_gen;
13476707Sbrutus 
13486707Sbrutus 	} else {
13496707Sbrutus 		generation = ring->cr_cmpl_gen;
13506707Sbrutus 	}
13516707Sbrutus 
13526707Sbrutus 	mutex_exit(&ring->cr_cmpl_mutex);
13536707Sbrutus 
13546707Sbrutus 	/*
13556707Sbrutus 	 * if cmd isn't passed in, well return.  Useful for updating the
13566707Sbrutus 	 * consumer pointer (ring->cr_cmpl_last).
13576707Sbrutus 	 */
1358*9638SRandy.Fishel@Sun.COM 	if (cmd->dp_flags & DCOPY_CMD_SYNC) {
13596707Sbrutus 		return (DCOPY_PENDING);
13606707Sbrutus 	}
13616707Sbrutus 
13626707Sbrutus 	/*
13636707Sbrutus 	 * if the post's generation is old, this post has completed. No reason
13646707Sbrutus 	 * to go check the last completion. if the generation is the same
13656707Sbrutus 	 * and if the post is before or = to the last completion processed,
13666707Sbrutus 	 * the post has completed.
13676707Sbrutus 	 */
13686707Sbrutus 	if (priv->ip_generation < generation) {
13696707Sbrutus 		return (DCOPY_COMPLETED);
13706707Sbrutus 	} else if ((priv->ip_generation == generation) &&
13716707Sbrutus 	    (priv->ip_index <= last_cmpl)) {
13726707Sbrutus 		return (DCOPY_COMPLETED);
13736707Sbrutus 	}
13746707Sbrutus 
13756707Sbrutus 	return (DCOPY_PENDING);
13766707Sbrutus }
13776707Sbrutus 
13786707Sbrutus 
13796707Sbrutus /*
13806707Sbrutus  * ioat_ring_reserve()
13816707Sbrutus  */
13826707Sbrutus int
ioat_ring_reserve(ioat_channel_t channel,ioat_channel_ring_t * ring,dcopy_cmd_t cmd)13836707Sbrutus ioat_ring_reserve(ioat_channel_t channel, ioat_channel_ring_t *ring,
13846707Sbrutus     dcopy_cmd_t cmd)
13856707Sbrutus {
13866707Sbrutus 	uint64_t dest_addr;
13876707Sbrutus 	uint32_t dest_size;
13886707Sbrutus 	uint64_t src_addr;
13896707Sbrutus 	uint32_t src_size;
13906707Sbrutus 	size_t xfer_size;
13916707Sbrutus 	uint64_t desc;
13926707Sbrutus 	int num_desc;
13936707Sbrutus 	size_t size;
13946707Sbrutus 	int i;
13956707Sbrutus 
13966707Sbrutus 
13976707Sbrutus 	/*
13986707Sbrutus 	 * figure out how many descriptors we need. This can include a dca
13996707Sbrutus 	 * desc and multiple desc for a dma copy.
14006707Sbrutus 	 */
14016707Sbrutus 	num_desc = 0;
14026707Sbrutus 	if ((channel->ic_ver == IOAT_CBv2) &&
14036707Sbrutus 	    (cmd->dp_flags & DCOPY_CMD_DCA)) {
14046707Sbrutus 		num_desc++;
14056707Sbrutus 	}
14066707Sbrutus 	src_addr = cmd->dp.copy.cc_source;
14076707Sbrutus 	dest_addr = cmd->dp.copy.cc_dest;
14086707Sbrutus 	size = cmd->dp.copy.cc_size;
14096707Sbrutus 	while (size > 0) {
14106707Sbrutus 		num_desc++;
14116707Sbrutus 
14126707Sbrutus 		/* adjust for any offset into the page */
14136707Sbrutus 		if ((src_addr & PAGEOFFSET) == 0) {
14146707Sbrutus 			src_size = PAGESIZE;
14156707Sbrutus 		} else {
14166707Sbrutus 			src_size = PAGESIZE - (src_addr & PAGEOFFSET);
14176707Sbrutus 		}
14186707Sbrutus 		if ((dest_addr & PAGEOFFSET) == 0) {
14196707Sbrutus 			dest_size = PAGESIZE;
14206707Sbrutus 		} else {
14216707Sbrutus 			dest_size = PAGESIZE - (dest_addr & PAGEOFFSET);
14226707Sbrutus 		}
14236707Sbrutus 
14246707Sbrutus 		/* take the smallest of the three */
14256707Sbrutus 		xfer_size = MIN(src_size, dest_size);
14266707Sbrutus 		xfer_size = MIN(xfer_size, size);
14276707Sbrutus 
14286707Sbrutus 		/* go to the next page */
14296707Sbrutus 		src_addr += xfer_size;
14306707Sbrutus 		dest_addr += xfer_size;
14316707Sbrutus 		size -= xfer_size;
14326707Sbrutus 	}
14336707Sbrutus 
14346707Sbrutus 	/* Make sure we have space for these descriptors */
14356707Sbrutus 	desc = ring->cr_desc_next;
14366707Sbrutus 	for (i = 0; i < num_desc; i++) {
14376707Sbrutus 
14386707Sbrutus 		/*
14396707Sbrutus 		 * if this is the last descriptor in the ring, see if the
14406707Sbrutus 		 * last completed descriptor is #0.
14416707Sbrutus 		 */
14426707Sbrutus 		if (desc == ring->cr_desc_last) {
14436707Sbrutus 			if (ring->cr_cmpl_last == 0) {
14446707Sbrutus 				/*
14456707Sbrutus 				 * if we think the ring is full, update where
14466707Sbrutus 				 * the H/W really is and check for full again.
14476707Sbrutus 				 */
1448*9638SRandy.Fishel@Sun.COM 				cmd->dp_flags |= DCOPY_CMD_SYNC;
1449*9638SRandy.Fishel@Sun.COM 				(void) ioat_cmd_poll(channel, cmd);
1450*9638SRandy.Fishel@Sun.COM 				cmd->dp_flags &= ~DCOPY_CMD_SYNC;
14516707Sbrutus 				if (ring->cr_cmpl_last == 0) {
14526707Sbrutus 					return (DCOPY_NORESOURCES);
14536707Sbrutus 				}
14546707Sbrutus 			}
14556707Sbrutus 
14566707Sbrutus 			/*
14576707Sbrutus 			 * go to the next descriptor which is zero in this
14586707Sbrutus 			 * case.
14596707Sbrutus 			 */
14606707Sbrutus 			desc = 0;
14616707Sbrutus 
14626707Sbrutus 		/*
14636707Sbrutus 		 * if this is not the last descriptor in the ring, see if
14646707Sbrutus 		 * the last completion we saw was the next descriptor.
14656707Sbrutus 		 */
14666707Sbrutus 		} else {
14676707Sbrutus 			if ((desc + 1) == ring->cr_cmpl_last) {
14686707Sbrutus 				/*
14696707Sbrutus 				 * if we think the ring is full, update where
14706707Sbrutus 				 * the H/W really is and check for full again.
14716707Sbrutus 				 */
1472*9638SRandy.Fishel@Sun.COM 				cmd->dp_flags |= DCOPY_CMD_SYNC;
1473*9638SRandy.Fishel@Sun.COM 				(void) ioat_cmd_poll(channel, cmd);
1474*9638SRandy.Fishel@Sun.COM 				cmd->dp_flags &= ~DCOPY_CMD_SYNC;
14756707Sbrutus 				if ((desc + 1) == ring->cr_cmpl_last) {
14766707Sbrutus 					return (DCOPY_NORESOURCES);
14776707Sbrutus 				}
14786707Sbrutus 			}
14796707Sbrutus 
14806707Sbrutus 			/* go to the next descriptor */
14816707Sbrutus 			desc++;
14826707Sbrutus 		}
14836707Sbrutus 	}
14846707Sbrutus 
14856707Sbrutus 	return (DCOPY_SUCCESS);
14866707Sbrutus }
1487