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