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