root/usr/src/uts/common/io/xge/hal/xgehal/xgehal-channel.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 * Copyright (c) 2002-2006 Neterion, Inc.
 */

#include "xgehal-channel.h"
#include "xgehal-fifo.h"
#include "xgehal-ring.h"
#include "xgehal-device.h"
#include "xgehal-regs.h"

/*
 * __hal_channel_dtr_next_reservelist
 *
 * Walking through the all available DTRs.
 */
static xge_hal_status_e
__hal_channel_dtr_next_reservelist(xge_hal_channel_h channelh,
                xge_hal_dtr_h *dtrh)
{
        xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;

        if (channel->reserve_top >= channel->reserve_length) {
                return XGE_HAL_INF_NO_MORE_FREED_DESCRIPTORS;
        }

        *dtrh = channel->reserve_arr[channel->reserve_top++];

        return XGE_HAL_OK;
}

/*
 * __hal_channel_dtr_next_freelist
 *
 * Walking through the "freed" DTRs.
 */
static xge_hal_status_e
__hal_channel_dtr_next_freelist(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh)
{
        xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;

        if (channel->reserve_initial == channel->free_length) {
                return XGE_HAL_INF_NO_MORE_FREED_DESCRIPTORS;
        }

        *dtrh = channel->free_arr[channel->free_length++];

        return XGE_HAL_OK;
}

/*
 * __hal_channel_dtr_next_not_completed - Get the _next_ posted but
 *                                     not completed descriptor.
 *
 * Walking through the "not completed" DTRs.
 */
static xge_hal_status_e
__hal_channel_dtr_next_not_completed(xge_hal_channel_h channelh,
                xge_hal_dtr_h *dtrh)
{
        /* LINTED E_FUNC_SET_NOT_USED */
        xge_hal_ring_rxd_1_t *rxdp __unused; /* doesn't matter 1, 3 or 5... */

        __hal_channel_dtr_try_complete(channelh, dtrh);
        if (*dtrh == NULL) {
                return XGE_HAL_INF_NO_MORE_COMPLETED_DESCRIPTORS;
        }

        rxdp = (xge_hal_ring_rxd_1_t *)*dtrh;
        xge_assert(rxdp->host_control!=0);

        __hal_channel_dtr_complete(channelh);

        return XGE_HAL_OK;
}

xge_hal_channel_t*
__hal_channel_allocate(xge_hal_device_h devh, int post_qid,
                xge_hal_channel_type_e type)
{
        xge_hal_device_t *hldev = (xge_hal_device_t*)devh;
        xge_hal_channel_t *channel;
        int size = 0;

        switch(type) {
                case XGE_HAL_CHANNEL_TYPE_FIFO:
                        xge_assert(post_qid + 1 >= XGE_HAL_MIN_FIFO_NUM &&
                                 post_qid + 1 <= XGE_HAL_MAX_FIFO_NUM);
                        size = sizeof(xge_hal_fifo_t);
                        break;
                case XGE_HAL_CHANNEL_TYPE_RING:
                        xge_assert(post_qid + 1 >= XGE_HAL_MIN_RING_NUM &&
                                post_qid + 1 <= XGE_HAL_MAX_RING_NUM);
                        size = sizeof(xge_hal_ring_t);
                        break;
                default :
                        xge_assert(size);
                        break;

        }


        /* allocate FIFO channel */
        channel = (xge_hal_channel_t *) xge_os_malloc(hldev->pdev, size);
        if (channel == NULL) {
                return NULL;
        }
        xge_os_memzero(channel, size);

        channel->pdev           = hldev->pdev;
        channel->regh0          = hldev->regh0;
        channel->regh1          = hldev->regh1;
        channel->type           = type;
        channel->devh           = devh;
        channel->post_qid       = post_qid;
        channel->compl_qid      = 0;

        return channel;
}

void __hal_channel_free(xge_hal_channel_t *channel)
{
        int size = 0;

        xge_assert(channel->pdev);

        switch(channel->type) {
                case XGE_HAL_CHANNEL_TYPE_FIFO:
                        size = sizeof(xge_hal_fifo_t);
                        break;
                case XGE_HAL_CHANNEL_TYPE_RING:
                        size = sizeof(xge_hal_ring_t);
                        break;
                case XGE_HAL_CHANNEL_TYPE_SEND_QUEUE:
                case XGE_HAL_CHANNEL_TYPE_RECEIVE_QUEUE:
                case XGE_HAL_CHANNEL_TYPE_COMPLETION_QUEUE:
                case XGE_HAL_CHANNEL_TYPE_UP_MESSAGE_QUEUE:
                case XGE_HAL_CHANNEL_TYPE_DOWN_MESSAGE_QUEUE:
                        xge_assert(size);
                        break;
                default:
                        break;
        }

        xge_os_free(channel->pdev, channel, size);
}

xge_hal_status_e
__hal_channel_initialize (xge_hal_channel_h channelh,
                xge_hal_channel_attr_t *attr, void **reserve_arr,
                int reserve_initial, int reserve_max, int reserve_threshold)
{
        xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
        xge_hal_device_t *hldev;

        hldev = (xge_hal_device_t *)channel->devh;

        channel->dtr_term = attr->dtr_term;
        channel->dtr_init = attr->dtr_init;
        channel->callback = attr->callback;
        channel->userdata = attr->userdata;
        channel->flags = attr->flags;
        channel->per_dtr_space = attr->per_dtr_space;

        channel->reserve_arr = reserve_arr;
        channel->reserve_initial = reserve_initial;
        channel->reserve_max = reserve_max;
        channel->reserve_length = channel->reserve_initial;
        channel->reserve_threshold = reserve_threshold;
        channel->reserve_top = 0;
        channel->saved_arr = (void **) xge_os_malloc(hldev->pdev,
                                           sizeof(void*)*channel->reserve_max);
        if (channel->saved_arr == NULL) {
                return XGE_HAL_ERR_OUT_OF_MEMORY;
        }
        xge_os_memzero(channel->saved_arr, sizeof(void*)*channel->reserve_max);
        channel->free_arr = channel->saved_arr;
        channel->free_length = channel->reserve_initial;
        channel->work_arr = (void **) xge_os_malloc(hldev->pdev,
                                  sizeof(void*)*channel->reserve_max);
        if (channel->work_arr == NULL) {
                return XGE_HAL_ERR_OUT_OF_MEMORY;
        }
        xge_os_memzero(channel->work_arr,
                       sizeof(void*)*channel->reserve_max);
        channel->post_index = 0;
        channel->compl_index = 0;
        channel->length = channel->reserve_initial;

        channel->orig_arr = (void **) xge_os_malloc(hldev->pdev,
                                                sizeof(void*)*channel->reserve_max);
        if (channel->orig_arr == NULL)
                return XGE_HAL_ERR_OUT_OF_MEMORY;

        xge_os_memzero(channel->orig_arr, sizeof(void*)*channel->reserve_max);

#if defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
        xge_os_spin_lock_init_irq(&channel->free_lock, hldev->irqh);
#elif defined(XGE_HAL_RX_MULTI_FREE) || defined(XGE_HAL_TX_MULTI_FREE)
        xge_os_spin_lock_init(&channel->free_lock, hldev->pdev);
#endif

        return XGE_HAL_OK;
}

void __hal_channel_terminate(xge_hal_channel_h channelh)
{
        xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
        xge_hal_device_t *hldev;

        hldev = (xge_hal_device_t *)channel->devh;

        xge_assert(channel->pdev);
        /* undo changes made at channel_initialize() */
        if (channel->work_arr) {
                xge_os_free(channel->pdev, channel->work_arr,
                          sizeof(void*)*channel->reserve_max);
                channel->work_arr = NULL;
        }

        if (channel->saved_arr) {
                xge_os_free(channel->pdev, channel->saved_arr,
                          sizeof(void*)*channel->reserve_max);
                channel->saved_arr = NULL;
        }

        if (channel->orig_arr) {
                xge_os_free(channel->pdev, channel->orig_arr,
                          sizeof(void*)*channel->reserve_max);
                channel->orig_arr = NULL;
        }

#if defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
        xge_os_spin_lock_destroy_irq(&channel->free_lock, hldev->irqh);
#elif defined(XGE_HAL_RX_MULTI_FREE) || defined(XGE_HAL_TX_MULTI_FREE)
        xge_os_spin_lock_destroy(&channel->free_lock, hldev->pdev);
#endif
}

/**
 * xge_hal_channel_open - Open communication channel.
 * @devh: HAL device, pointer to xge_hal_device_t structure.
 * @attr: Contains attributes required to open
 *        the channel.
 * @channelh:  The channel handle. On success (XGE_HAL_OK) HAL fills
 * this "out" parameter with a valid channel handle.
 * @reopen: See  xge_hal_channel_reopen_e{}.
 *
 * Open communication channel with the device.
 *
 * HAL uses (persistent) channel configuration to allocate both channel
 * and Xframe Tx and Rx descriptors.
 * Notes:
 *     1) The channel config data is fed into HAL prior to
 *        xge_hal_channel_open().
 *
 *     2) The corresponding hardware queues must be already configured and
 *        enabled.
 *
 *     3) Either down or up queue may be omitted, in which case the channel
 *        is treated as _unidirectional_.
 *
 *     4) Post and completion queue may be the same, in which case the channel
 *        is said to have "in-band completions".
 *
 * Note that free_channels list is not protected. i.e. caller must provide
 * safe context.
 *
 * Returns: XGE_HAL_OK  - success.
 * XGE_HAL_ERR_CHANNEL_NOT_FOUND - Unable to locate the channel.
 * XGE_HAL_ERR_OUT_OF_MEMORY - Memory allocation failed.
 *
 * See also: xge_hal_channel_attr_t{}.
 * Usage: See ex_open{}.
 */
xge_hal_status_e
xge_hal_channel_open(xge_hal_device_h devh,
                     xge_hal_channel_attr_t *attr,
                     xge_hal_channel_h *channelh,
                     xge_hal_channel_reopen_e reopen)
{
        xge_list_t *item;
        int i;
        xge_hal_status_e status = XGE_HAL_OK;
        xge_hal_channel_t *channel = NULL;
        xge_hal_device_t *device = (xge_hal_device_t *)devh;

        xge_assert(device);
        xge_assert(attr);

        *channelh = NULL;

        /* find channel */
                xge_list_for_each(item, &device->free_channels) {
                        xge_hal_channel_t *tmp;

                        tmp = xge_container_of(item, xge_hal_channel_t, item);
                        if (tmp->type == attr->type &&
                        tmp->post_qid == attr->post_qid &&
                        tmp->compl_qid == attr->compl_qid) {
                                channel = tmp;
                                break;
                        }
                }

                if (channel == NULL) {
                        return XGE_HAL_ERR_CHANNEL_NOT_FOUND;
                }

        xge_assert((channel->type == XGE_HAL_CHANNEL_TYPE_FIFO) ||
                (channel->type == XGE_HAL_CHANNEL_TYPE_RING));

        if (reopen == XGE_HAL_CHANNEL_OC_NORMAL) {
                /* allocate memory, initialize pointers, etc */
                switch(channel->type) {
                        case XGE_HAL_CHANNEL_TYPE_FIFO:
                                status = __hal_fifo_open(channel, attr);
                                break;
                        case XGE_HAL_CHANNEL_TYPE_RING:
                                status = __hal_ring_open(channel, attr);
                                break;
                        case XGE_HAL_CHANNEL_TYPE_SEND_QUEUE:
                        case XGE_HAL_CHANNEL_TYPE_RECEIVE_QUEUE:
                        case XGE_HAL_CHANNEL_TYPE_COMPLETION_QUEUE:
                        case XGE_HAL_CHANNEL_TYPE_UP_MESSAGE_QUEUE:
                        case XGE_HAL_CHANNEL_TYPE_DOWN_MESSAGE_QUEUE:
                                status = XGE_HAL_FAIL;
                                break;
                        default:
                                break;
                }

                if (status == XGE_HAL_OK) {
                        for (i = 0; i < channel->reserve_initial; i++) {
                                channel->orig_arr[i] =
                                        channel->reserve_arr[i];
                        }
                }
                else
                        return status;
        } else {
                xge_assert(reopen == XGE_HAL_CHANNEL_RESET_ONLY);

                for (i = 0; i < channel->reserve_initial; i++) {
                        channel->reserve_arr[i] = channel->orig_arr[i];
                        channel->free_arr[i] = NULL;
                }
                channel->free_length = channel->reserve_initial;
                channel->reserve_length = channel->reserve_initial;
                channel->reserve_top = 0;
                channel->post_index = 0;
                channel->compl_index = 0;
                if (channel->type == XGE_HAL_CHANNEL_TYPE_RING) {
                        status = __hal_ring_initial_replenish(channel,
                                                              reopen);
                        if (status != XGE_HAL_OK)
                                return status;
                }
        }

        /* move channel to the open state list */

        switch(channel->type) {
                case XGE_HAL_CHANNEL_TYPE_FIFO:
                        xge_list_remove(&channel->item);
                        xge_list_insert(&channel->item, &device->fifo_channels);
                        break;
                case XGE_HAL_CHANNEL_TYPE_RING:
                        xge_list_remove(&channel->item);
                        xge_list_insert(&channel->item, &device->ring_channels);
                        break;
                case XGE_HAL_CHANNEL_TYPE_SEND_QUEUE:
                case XGE_HAL_CHANNEL_TYPE_RECEIVE_QUEUE:
                case XGE_HAL_CHANNEL_TYPE_COMPLETION_QUEUE:
                case XGE_HAL_CHANNEL_TYPE_UP_MESSAGE_QUEUE:
                case XGE_HAL_CHANNEL_TYPE_DOWN_MESSAGE_QUEUE:
                        xge_assert(channel->type == XGE_HAL_CHANNEL_TYPE_FIFO ||
                                   channel->type == XGE_HAL_CHANNEL_TYPE_RING);
                        break;
                default:
                        break;
        }
        channel->is_open = 1;
        channel->terminating = 0;
        /*
         * The magic check the argument validity, has to be
         * removed before 03/01/2005.
         */
        channel->magic = XGE_HAL_MAGIC;

        *channelh = channel;

        return XGE_HAL_OK;
}

/**
 * xge_hal_channel_abort - Abort the channel.
 * @channelh: Channel handle.
 * @reopen: See  xge_hal_channel_reopen_e{}.
 *
 * Terminate (via xge_hal_channel_dtr_term_f{}) all channel descriptors.
 * Currently used internally only by HAL, as part of its
 * xge_hal_channel_close() and xge_hal_channel_open() in case
 * of fatal error.
 *
 * See also: xge_hal_channel_dtr_term_f{}.
 */
void xge_hal_channel_abort(xge_hal_channel_h channelh,
                           xge_hal_channel_reopen_e reopen)
{
        xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
        xge_hal_dtr_h dtr;
#ifdef XGE_OS_MEMORY_CHECK
        int check_cnt = 0;
#endif
        int free_length_sav;
        int reserve_top_sav;

        if (channel->dtr_term == NULL) {
                return;
        }

        free_length_sav = channel->free_length;
        while (__hal_channel_dtr_next_freelist(channelh, &dtr) == XGE_HAL_OK) {
#ifdef XGE_OS_MEMORY_CHECK
#ifdef XGE_DEBUG_ASSERT
                if (channel->type == XGE_HAL_CHANNEL_TYPE_FIFO) {
                    xge_assert(!__hal_fifo_txdl_priv(dtr)->allocated);
                } else {
                    if (channel->type == XGE_HAL_CHANNEL_TYPE_RING) {
                            xge_assert(!__hal_ring_rxd_priv((xge_hal_ring_t * ) channelh, dtr)->allocated);
                    }
                }
#endif
                check_cnt++;
#endif
                channel->dtr_term(channel, dtr, XGE_HAL_DTR_STATE_FREED,
                                  channel->userdata, reopen);
        }
        channel->free_length = free_length_sav;

        while (__hal_channel_dtr_next_not_completed(channelh, &dtr) ==
               XGE_HAL_OK) {
#ifdef XGE_OS_MEMORY_CHECK
#ifdef XGE_DEBUG_ASSERT
                if (channel->type == XGE_HAL_CHANNEL_TYPE_FIFO) {
                        xge_assert(__hal_fifo_txdl_priv(dtr)->allocated);
                } else {
                    if (channel->type == XGE_HAL_CHANNEL_TYPE_RING) {
                        xge_assert(__hal_ring_rxd_priv((xge_hal_ring_t * ) channelh, dtr)
                                   ->allocated);
                    }
                }
#endif
                check_cnt++;
#endif
                channel->dtr_term(channel, dtr, XGE_HAL_DTR_STATE_POSTED,
                                  channel->userdata, reopen);

        }

        reserve_top_sav = channel->reserve_top;
        while (__hal_channel_dtr_next_reservelist(channelh, &dtr) ==
                                                        XGE_HAL_OK) {
#ifdef XGE_OS_MEMORY_CHECK
#ifdef XGE_DEBUG_ASSERT
                if (channel->type == XGE_HAL_CHANNEL_TYPE_FIFO) {
                    xge_assert(!__hal_fifo_txdl_priv(dtr)->allocated);
                } else {
                    if (channel->type == XGE_HAL_CHANNEL_TYPE_RING) {
                        xge_assert(!__hal_ring_rxd_priv((xge_hal_ring_t * ) channelh, dtr)->allocated);
                    }
                }
#endif
                check_cnt++;
#endif
                channel->dtr_term(channel, dtr, XGE_HAL_DTR_STATE_AVAIL,
                                  channel->userdata, reopen);
        }
        channel->reserve_top = reserve_top_sav;

        xge_assert(channel->reserve_length ==
                (channel->free_length + channel->reserve_top));

#ifdef XGE_OS_MEMORY_CHECK
        xge_assert(check_cnt == channel->reserve_initial);
#endif

}

/**
 * xge_hal_channel_close - Close communication channel.
 * @channelh: The channel handle.
 * @reopen: See  xge_hal_channel_reopen_e{}.
 *
 * Will close previously opened channel and deallocate associated resources.
 * Channel must be opened otherwise assert will be generated.
 * Note that free_channels list is not protected. i.e. caller must provide
 * safe context.
 */
void xge_hal_channel_close(xge_hal_channel_h channelh,
                           xge_hal_channel_reopen_e reopen)
{
        xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
        xge_hal_device_t *hldev;
        xge_list_t *item;
        xge_assert(channel);
        xge_assert(channel->type < XGE_HAL_CHANNEL_TYPE_MAX);

        hldev = (xge_hal_device_t *)channel->devh;
        channel->is_open = 0;
        channel->magic = XGE_HAL_DEAD;

                /* sanity check: make sure channel is not in free list */
                xge_list_for_each(item, &hldev->free_channels) {
                        xge_hal_channel_t *tmp;

                        tmp = xge_container_of(item, xge_hal_channel_t, item);
                        xge_assert(!tmp->is_open);
                        if (channel == tmp) {
                                return;
                        }
                }

        xge_hal_channel_abort(channel, reopen);

        xge_assert((channel->type == XGE_HAL_CHANNEL_TYPE_FIFO) ||
                   (channel->type == XGE_HAL_CHANNEL_TYPE_RING));

        if (reopen == XGE_HAL_CHANNEL_OC_NORMAL) {
                /* de-allocate */
                switch(channel->type) {
                        case XGE_HAL_CHANNEL_TYPE_FIFO:
                                __hal_fifo_close(channelh);
                                break;
                        case XGE_HAL_CHANNEL_TYPE_RING:
                                __hal_ring_close(channelh);
                                break;
                        case XGE_HAL_CHANNEL_TYPE_SEND_QUEUE:
                        case XGE_HAL_CHANNEL_TYPE_RECEIVE_QUEUE:
                        case XGE_HAL_CHANNEL_TYPE_COMPLETION_QUEUE:
                        case XGE_HAL_CHANNEL_TYPE_UP_MESSAGE_QUEUE:
                        case XGE_HAL_CHANNEL_TYPE_DOWN_MESSAGE_QUEUE:
                                xge_assert(channel->type == XGE_HAL_CHANNEL_TYPE_FIFO ||
                                    channel->type == XGE_HAL_CHANNEL_TYPE_RING);
                                break;
                        default:
                                break;
                }
        }

        /* move channel back to free state list */
        xge_list_remove(&channel->item);
                xge_list_insert(&channel->item, &hldev->free_channels);

                if (xge_list_is_empty(&hldev->fifo_channels) &&
                        xge_list_is_empty(&hldev->ring_channels)) {
                        /* clear msix_idx in case of following HW reset */
                        hldev->reset_needed_after_close = 1;
                }
}