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