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
ioat_channel_init(ioat_state_t * state)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
ioat_channel_fini(ioat_state_t * state)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
ioat_channel_alloc(void * device_private,dcopy_handle_t handle,int flags,uint_t size,dcopy_query_channel_t * info,void * channel_private)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
ioat_channel_suspend(ioat_state_t * state)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
ioat_channel_resume(ioat_state_t * state)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
ioat_channel_quiesce(ioat_state_t * state)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
ioat_channel_free(void * channel_private)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
ioat_channel_intr(ioat_channel_t channel)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
ioat_channel_start(ioat_channel_t channel)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
ioat_channel_reset(ioat_channel_t channel)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
ioat_completion_alloc(ioat_channel_t channel)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
ioat_completion_free(ioat_channel_t channel)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
ioat_ring_alloc(ioat_channel_t channel,uint_t desc_cnt)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
ioat_ring_free(ioat_channel_t channel)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
ioat_ring_seed(ioat_channel_t channel,ioat_chan_dma_desc_t * in_desc)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
ioat_ring_loop(ioat_channel_ring_t * ring,dcopy_cmd_t cmd)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
ioat_cmd_alloc(void * private,int flags,dcopy_cmd_t * cmd)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
ioat_cmd_free(void * private,dcopy_cmd_t * cmdp)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
ioat_cmd_post(void * private,dcopy_cmd_t cmd)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
ioat_cmd_post_dca(ioat_channel_ring_t * ring,uint32_t dca_id)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
ioat_cmd_post_copy(ioat_channel_ring_t * ring,uint64_t src_addr,uint64_t dest_addr,uint32_t size,uint32_t ctrl)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
ioat_cmd_poll(void * private,dcopy_cmd_t cmd)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
ioat_ring_reserve(ioat_channel_t channel,ioat_channel_ring_t * ring,dcopy_cmd_t cmd)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