df4d502619
changes: 01 - Enhanced LRO: LRO feature is extended to support multi-buffer mode. Previously, Ethernet frames received in contiguous buffers were offloaded. Now, frames received in multiple non-contiguous buffers can be offloaded, as well. The driver now supports LRO for jumbo frames. 02 - Locks Optimization: The driver code was re-organized to limit the use of locks. Moreover, lock contention was reduced by replacing wait locks with try locks. 03 - Code Optimization: The driver code was re-factored to eliminate some memcpy operations. Fast path loops were optimized. 04 - Tag Creations: Physical Buffer Tags are now optimized based upon frame size. For better performance, Physical Memory Maps are now re-used. 05 - Configuration: Features such as TSO, LRO, and Interrupt Mode can be configured either at load or at run time. Rx buffer mode (mode 1 or mode 2) can be configured at load time through kenv. 06 - Driver Statistics: Run time statistics are enhanced to provide better visibility into the driver performance. 07 - Bug Fixes: The driver contains fixes for the problems discovered and reported since last submission. 08 - MSI support: Added Message Signaled Interrupt feature which currently uses 1 message. 09 Removed feature: Rx 3 buffer mode feature has been removed. Driver now supports 1, 2 and 5 buffer modes of which 2 and 5 buffer modes can be used for header separation. 10 Compiler warning: Fixed compiler warning when compiled for 32 bit system. 11 Copyright notice: Source files are updated with the proper copyright notice. MFC after: 3 days Submitted by: Alicia Pena <Alicia dot Pena at neterion dot com>, Muhammad Shafiq <Muhammad dot Shafiq at neterion dot com>
1168 lines
39 KiB
C
1168 lines
39 KiB
C
/*-
|
||
* Copyright (c) 2002-2007 Neterion, Inc.
|
||
* All rights reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions
|
||
* are met:
|
||
* 1. Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* 2. Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditions and the following disclaimer in the
|
||
* documentation and/or other materials provided with the distribution.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
* SUCH DAMAGE.
|
||
*
|
||
* $FreeBSD$
|
||
*/
|
||
|
||
#ifdef XGE_DEBUG_FP
|
||
#include <dev/nxge/include/xgehal-fifo.h>
|
||
#endif
|
||
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_fifo_txdl_priv_t*
|
||
__hal_fifo_txdl_priv(xge_hal_dtr_h dtrh)
|
||
{
|
||
xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t*)dtrh;
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv;
|
||
|
||
xge_assert(txdp);
|
||
txdl_priv = (xge_hal_fifo_txdl_priv_t *)
|
||
(ulong_t)txdp->host_control;
|
||
|
||
xge_assert(txdl_priv);
|
||
xge_assert(txdl_priv->dma_object);
|
||
xge_assert(txdl_priv->dma_addr);
|
||
|
||
xge_assert(txdl_priv->dma_object->handle == txdl_priv->dma_handle);
|
||
|
||
return txdl_priv;
|
||
}
|
||
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void
|
||
__hal_fifo_dtr_post_single(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh,
|
||
u64 ctrl_1)
|
||
{
|
||
xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh;
|
||
xge_hal_fifo_hw_pair_t *hw_pair = fifo->hw_pair;
|
||
xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)dtrh;
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv;
|
||
u64 ctrl;
|
||
|
||
txdp->control_1 |= XGE_HAL_TXD_LIST_OWN_XENA;
|
||
|
||
#ifdef XGE_DEBUG_ASSERT
|
||
/* make sure Xena overwrites the (illegal) t_code value on completion */
|
||
XGE_HAL_SET_TXD_T_CODE(txdp->control_1, XGE_HAL_TXD_T_CODE_UNUSED_5);
|
||
#endif
|
||
|
||
txdl_priv = __hal_fifo_txdl_priv(dtrh);
|
||
|
||
#if defined(XGE_OS_DMA_REQUIRES_SYNC) && defined(XGE_HAL_DMA_DTR_STREAMING)
|
||
/* sync the TxDL to device */
|
||
xge_os_dma_sync(fifo->channel.pdev,
|
||
txdl_priv->dma_handle,
|
||
txdl_priv->dma_addr,
|
||
txdl_priv->dma_offset,
|
||
txdl_priv->frags << 5 /* sizeof(xge_hal_fifo_txd_t) */,
|
||
XGE_OS_DMA_DIR_TODEVICE);
|
||
#endif
|
||
/* write the pointer first */
|
||
xge_os_pio_mem_write64(fifo->channel.pdev,
|
||
fifo->channel.regh1,
|
||
txdl_priv->dma_addr,
|
||
&hw_pair->txdl_pointer);
|
||
|
||
/* spec: 0x00 = 1 TxD in the list */
|
||
ctrl = XGE_HAL_TX_FIFO_LAST_TXD_NUM(txdl_priv->frags - 1);
|
||
ctrl |= ctrl_1;
|
||
ctrl |= fifo->no_snoop_bits;
|
||
|
||
if (txdp->control_1 & XGE_HAL_TXD_LSO_COF_CTRL(XGE_HAL_TXD_TCP_LSO)) {
|
||
ctrl |= XGE_HAL_TX_FIFO_SPECIAL_FUNC;
|
||
}
|
||
|
||
/*
|
||
* according to the XENA spec:
|
||
*
|
||
* It is important to note that pointers and list control words are
|
||
* always written in pairs: in the first write, the host must write a
|
||
* pointer, and in the second write, it must write the list control
|
||
* word. Any other access will result in an error. Also, all 16 bytes
|
||
* of the pointer/control structure must be written, including any
|
||
* reserved bytes.
|
||
*/
|
||
xge_os_wmb();
|
||
|
||
/*
|
||
* we want touch work_arr in order with ownership bit set to HW
|
||
*/
|
||
__hal_channel_dtr_post(channelh, dtrh);
|
||
|
||
xge_os_pio_mem_write64(fifo->channel.pdev, fifo->channel.regh1,
|
||
ctrl, &hw_pair->list_control);
|
||
|
||
xge_debug_fifo(XGE_TRACE, "posted txdl 0x"XGE_OS_LLXFMT" ctrl 0x"XGE_OS_LLXFMT" "
|
||
"into 0x"XGE_OS_LLXFMT"", (unsigned long long)txdl_priv->dma_addr,
|
||
(unsigned long long)ctrl,
|
||
(unsigned long long)(ulong_t)&hw_pair->txdl_pointer);
|
||
|
||
#ifdef XGE_HAL_FIFO_DUMP_TXD
|
||
xge_os_printf(""XGE_OS_LLXFMT":"XGE_OS_LLXFMT":"XGE_OS_LLXFMT":"
|
||
XGE_OS_LLXFMT" dma "XGE_OS_LLXFMT,
|
||
txdp->control_1, txdp->control_2, txdp->buffer_pointer,
|
||
txdp->host_control, txdl_priv->dma_addr);
|
||
#endif
|
||
|
||
fifo->channel.stats.total_posts++;
|
||
fifo->channel.usage_cnt++;
|
||
if (fifo->channel.stats.usage_max < fifo->channel.usage_cnt)
|
||
fifo->channel.stats.usage_max = fifo->channel.usage_cnt;
|
||
}
|
||
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void
|
||
__hal_fifo_txdl_free_many(xge_hal_channel_h channelh,
|
||
xge_hal_fifo_txd_t *txdp, int list_size, int frags)
|
||
{
|
||
xge_hal_fifo_txdl_priv_t *current_txdl_priv;
|
||
xge_hal_fifo_txdl_priv_t *next_txdl_priv;
|
||
int invalid_frags = frags % list_size;
|
||
if (invalid_frags){
|
||
xge_debug_fifo(XGE_ERR,
|
||
"freeing corrupt dtrh %p, fragments %d list size %d",
|
||
txdp, frags, list_size);
|
||
xge_assert(invalid_frags == 0);
|
||
}
|
||
while(txdp){
|
||
xge_debug_fifo(XGE_TRACE,
|
||
"freeing linked dtrh %p, fragments %d list size %d",
|
||
txdp, frags, list_size);
|
||
current_txdl_priv = __hal_fifo_txdl_priv(txdp);
|
||
#if defined(XGE_DEBUG_ASSERT) && defined(XGE_OS_MEMORY_CHECK)
|
||
current_txdl_priv->allocated = 0;
|
||
#endif
|
||
__hal_channel_dtr_free(channelh, txdp);
|
||
next_txdl_priv = current_txdl_priv->next_txdl_priv;
|
||
xge_assert(frags);
|
||
frags -= list_size;
|
||
if (next_txdl_priv) {
|
||
current_txdl_priv->next_txdl_priv = NULL;
|
||
txdp = next_txdl_priv->first_txdp;
|
||
}
|
||
else {
|
||
xge_debug_fifo(XGE_TRACE,
|
||
"freed linked dtrh fragments %d list size %d",
|
||
frags, list_size);
|
||
break;
|
||
}
|
||
}
|
||
xge_assert(frags == 0)
|
||
}
|
||
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void
|
||
__hal_fifo_txdl_restore_many(xge_hal_channel_h channelh,
|
||
xge_hal_fifo_txd_t *txdp, int txdl_count)
|
||
{
|
||
xge_hal_fifo_txdl_priv_t *current_txdl_priv;
|
||
xge_hal_fifo_txdl_priv_t *next_txdl_priv;
|
||
int i = txdl_count;
|
||
|
||
xge_assert(((xge_hal_channel_t *)channelh)->reserve_length +
|
||
txdl_count <= ((xge_hal_channel_t *)channelh)->reserve_initial);
|
||
|
||
current_txdl_priv = __hal_fifo_txdl_priv(txdp);
|
||
do{
|
||
xge_assert(i);
|
||
#if defined(XGE_DEBUG_ASSERT) && defined(XGE_OS_MEMORY_CHECK)
|
||
current_txdl_priv->allocated = 0;
|
||
#endif
|
||
next_txdl_priv = current_txdl_priv->next_txdl_priv;
|
||
txdp = current_txdl_priv->first_txdp;
|
||
current_txdl_priv->next_txdl_priv = NULL;
|
||
__hal_channel_dtr_restore(channelh, (xge_hal_dtr_h )txdp, --i);
|
||
xge_debug_fifo(XGE_TRACE,
|
||
"dtrh %p restored at offset %d", txdp, i);
|
||
current_txdl_priv = next_txdl_priv;
|
||
} while(current_txdl_priv);
|
||
__hal_channel_dtr_restore(channelh, NULL, txdl_count);
|
||
}
|
||
/**
|
||
* xge_hal_fifo_dtr_private - Retrieve per-descriptor private data.
|
||
* @channelh: Channel handle.
|
||
* @dtrh: Descriptor handle.
|
||
*
|
||
* Retrieve per-descriptor private data.
|
||
* Note that ULD requests per-descriptor space via
|
||
* xge_hal_channel_open().
|
||
*
|
||
* Returns: private ULD data associated with the descriptor.
|
||
* Usage: See ex_xmit{} and ex_tx_compl{}.
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void*
|
||
xge_hal_fifo_dtr_private(xge_hal_dtr_h dtrh)
|
||
{
|
||
xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)dtrh;
|
||
|
||
return ((char *)(ulong_t)txdp->host_control) +
|
||
sizeof(xge_hal_fifo_txdl_priv_t);
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_buffer_cnt - Get number of buffers carried by the
|
||
* descriptor.
|
||
* @dtrh: Descriptor handle.
|
||
*
|
||
* Returns: Number of buffers stored in the given descriptor. Can be used
|
||
* _after_ the descriptor is set up for posting (see
|
||
* xge_hal_fifo_dtr_post()) and _before_ it is deallocated (see
|
||
* xge_hal_fifo_dtr_free()).
|
||
*
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO int
|
||
xge_hal_fifo_dtr_buffer_cnt(xge_hal_dtr_h dtrh)
|
||
{
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv;
|
||
|
||
txdl_priv = __hal_fifo_txdl_priv(dtrh);
|
||
|
||
return txdl_priv->frags;
|
||
}
|
||
/**
|
||
* xge_hal_fifo_dtr_reserve_many- Reserve fifo descriptors which span more
|
||
* than single txdl.
|
||
* @channelh: Channel handle.
|
||
* @dtrh: Reserved descriptor. On success HAL fills this "out" parameter
|
||
* with a valid handle.
|
||
* @frags: minimum number of fragments to be reserved.
|
||
*
|
||
* Reserve TxDL(s) (that is, fifo descriptor)
|
||
* for the subsequent filling-in by upper layerdriver (ULD))
|
||
* and posting on the corresponding channel (@channelh)
|
||
* via xge_hal_fifo_dtr_post().
|
||
*
|
||
* Returns: XGE_HAL_OK - success;
|
||
* XGE_HAL_INF_OUT_OF_DESCRIPTORS - Currently no descriptors available
|
||
*
|
||
* See also: xge_hal_fifo_dtr_reserve_sp(), xge_hal_fifo_dtr_free(),
|
||
* xge_hal_ring_dtr_reserve(), xge_hal_status_e{}.
|
||
* Usage: See ex_xmit{}.
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e
|
||
xge_hal_fifo_dtr_reserve_many(xge_hal_channel_h channelh,
|
||
xge_hal_dtr_h *dtrh, const int frags)
|
||
{
|
||
xge_hal_status_e status = XGE_HAL_OK;
|
||
int alloc_frags = 0, dang_frags = 0;
|
||
xge_hal_fifo_txd_t *curr_txdp = NULL;
|
||
xge_hal_fifo_txd_t *next_txdp;
|
||
xge_hal_fifo_txdl_priv_t *next_txdl_priv, *curr_txdl_priv = NULL;
|
||
xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh;
|
||
int max_frags = fifo->config->max_frags;
|
||
xge_hal_dtr_h dang_dtrh = NULL;
|
||
#if defined(XGE_HAL_TX_MULTI_RESERVE_IRQ)
|
||
unsigned long flags=0;
|
||
#endif
|
||
xge_debug_fifo(XGE_TRACE, "dtr_reserve_many called for frags %d",
|
||
frags);
|
||
xge_assert(frags < (fifo->txdl_per_memblock * max_frags));
|
||
#if defined(XGE_HAL_TX_MULTI_RESERVE)
|
||
xge_os_spin_lock(&fifo->channel.reserve_lock);
|
||
#elif defined(XGE_HAL_TX_MULTI_RESERVE_IRQ)
|
||
xge_os_spin_lock_irq(&fifo->channel.reserve_lock, flags);
|
||
#endif
|
||
while(alloc_frags < frags) {
|
||
status = __hal_channel_dtr_alloc(channelh,
|
||
(xge_hal_dtr_h *)(void*)&next_txdp);
|
||
if (status != XGE_HAL_OK){
|
||
xge_debug_fifo(XGE_ERR,
|
||
"failed to allocate linked fragments rc %d",
|
||
status);
|
||
xge_assert(status == XGE_HAL_INF_OUT_OF_DESCRIPTORS);
|
||
if (*dtrh) {
|
||
xge_assert(alloc_frags/max_frags);
|
||
__hal_fifo_txdl_restore_many(channelh,
|
||
(xge_hal_fifo_txd_t *) *dtrh, alloc_frags/max_frags);
|
||
}
|
||
if (dang_dtrh) {
|
||
xge_assert(dang_frags/max_frags);
|
||
__hal_fifo_txdl_restore_many(channelh,
|
||
(xge_hal_fifo_txd_t *) dang_dtrh, dang_frags/max_frags);
|
||
}
|
||
break;
|
||
}
|
||
xge_debug_fifo(XGE_TRACE, "allocated linked dtrh %p"
|
||
" for frags %d", next_txdp, frags);
|
||
next_txdl_priv = __hal_fifo_txdl_priv(next_txdp);
|
||
xge_assert(next_txdl_priv);
|
||
xge_assert(next_txdl_priv->first_txdp == next_txdp);
|
||
next_txdl_priv->dang_txdl = NULL;
|
||
next_txdl_priv->dang_frags = 0;
|
||
next_txdl_priv->next_txdl_priv = NULL;
|
||
#if defined(XGE_OS_MEMORY_CHECK)
|
||
next_txdl_priv->allocated = 1;
|
||
#endif
|
||
if (!curr_txdp || !curr_txdl_priv) {
|
||
curr_txdp = next_txdp;
|
||
curr_txdl_priv = next_txdl_priv;
|
||
*dtrh = (xge_hal_dtr_h)next_txdp;
|
||
alloc_frags = max_frags;
|
||
continue;
|
||
}
|
||
if (curr_txdl_priv->memblock ==
|
||
next_txdl_priv->memblock) {
|
||
xge_debug_fifo(XGE_TRACE,
|
||
"linking dtrh %p, with %p",
|
||
*dtrh, next_txdp);
|
||
xge_assert (next_txdp ==
|
||
curr_txdp + max_frags);
|
||
alloc_frags += max_frags;
|
||
curr_txdl_priv->next_txdl_priv = next_txdl_priv;
|
||
}
|
||
else {
|
||
xge_assert(*dtrh);
|
||
xge_assert(dang_dtrh == NULL);
|
||
dang_dtrh = *dtrh;
|
||
dang_frags = alloc_frags;
|
||
xge_debug_fifo(XGE_TRACE,
|
||
"dangling dtrh %p, linked with dtrh %p",
|
||
*dtrh, next_txdp);
|
||
next_txdl_priv->dang_txdl = (xge_hal_fifo_txd_t *) *dtrh;
|
||
next_txdl_priv->dang_frags = alloc_frags;
|
||
alloc_frags = max_frags;
|
||
*dtrh = next_txdp;
|
||
}
|
||
curr_txdp = next_txdp;
|
||
curr_txdl_priv = next_txdl_priv;
|
||
}
|
||
|
||
#if defined(XGE_HAL_TX_MULTI_RESERVE)
|
||
xge_os_spin_unlock(&fifo->channel.reserve_lock);
|
||
#elif defined(XGE_HAL_TX_MULTI_RESERVE_IRQ)
|
||
xge_os_spin_unlock_irq(&fifo->channel.reserve_lock, flags);
|
||
#endif
|
||
|
||
if (status == XGE_HAL_OK) {
|
||
xge_hal_fifo_txdl_priv_t * txdl_priv;
|
||
xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)*dtrh;
|
||
xge_hal_stats_channel_info_t *statsp = &fifo->channel.stats;
|
||
txdl_priv = __hal_fifo_txdl_priv(txdp);
|
||
/* reset the TxDL's private */
|
||
txdl_priv->align_dma_offset = 0;
|
||
txdl_priv->align_vaddr_start = txdl_priv->align_vaddr;
|
||
txdl_priv->align_used_frags = 0;
|
||
txdl_priv->frags = 0;
|
||
txdl_priv->bytes_sent = 0;
|
||
txdl_priv->alloc_frags = alloc_frags;
|
||
/* reset TxD0 */
|
||
txdp->control_1 = txdp->control_2 = 0;
|
||
|
||
#if defined(XGE_OS_MEMORY_CHECK)
|
||
txdl_priv->allocated = 1;
|
||
#endif
|
||
/* update statistics */
|
||
statsp->total_posts_dtrs_many++;
|
||
statsp->total_posts_frags_many += txdl_priv->alloc_frags;
|
||
if (txdl_priv->dang_frags){
|
||
statsp->total_posts_dang_dtrs++;
|
||
statsp->total_posts_dang_frags += txdl_priv->dang_frags;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_reserve - Reserve fifo descriptor.
|
||
* @channelh: Channel handle.
|
||
* @dtrh: Reserved descriptor. On success HAL fills this "out" parameter
|
||
* with a valid handle.
|
||
*
|
||
* Reserve a single TxDL (that is, fifo descriptor)
|
||
* for the subsequent filling-in by upper layerdriver (ULD))
|
||
* and posting on the corresponding channel (@channelh)
|
||
* via xge_hal_fifo_dtr_post().
|
||
*
|
||
* Note: it is the responsibility of ULD to reserve multiple descriptors
|
||
* for lengthy (e.g., LSO) transmit operation. A single fifo descriptor
|
||
* carries up to configured number (fifo.max_frags) of contiguous buffers.
|
||
*
|
||
* Returns: XGE_HAL_OK - success;
|
||
* XGE_HAL_INF_OUT_OF_DESCRIPTORS - Currently no descriptors available
|
||
*
|
||
* See also: xge_hal_fifo_dtr_reserve_sp(), xge_hal_fifo_dtr_free(),
|
||
* xge_hal_ring_dtr_reserve(), xge_hal_status_e{}.
|
||
* Usage: See ex_xmit{}.
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e
|
||
xge_hal_fifo_dtr_reserve(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh)
|
||
{
|
||
xge_hal_status_e status;
|
||
#if defined(XGE_HAL_TX_MULTI_RESERVE_IRQ)
|
||
unsigned long flags=0;
|
||
#endif
|
||
|
||
#if defined(XGE_HAL_TX_MULTI_RESERVE)
|
||
xge_os_spin_lock(&((xge_hal_channel_t*)channelh)->reserve_lock);
|
||
#elif defined(XGE_HAL_TX_MULTI_RESERVE_IRQ)
|
||
xge_os_spin_lock_irq(&((xge_hal_channel_t*)channelh)->reserve_lock,
|
||
flags);
|
||
#endif
|
||
|
||
status = __hal_channel_dtr_alloc(channelh, dtrh);
|
||
|
||
#if defined(XGE_HAL_TX_MULTI_RESERVE)
|
||
xge_os_spin_unlock(&((xge_hal_channel_t*)channelh)->reserve_lock);
|
||
#elif defined(XGE_HAL_TX_MULTI_RESERVE_IRQ)
|
||
xge_os_spin_unlock_irq(&((xge_hal_channel_t*)channelh)->reserve_lock,
|
||
flags);
|
||
#endif
|
||
|
||
if (status == XGE_HAL_OK) {
|
||
xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)*dtrh;
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv;
|
||
|
||
txdl_priv = __hal_fifo_txdl_priv(txdp);
|
||
|
||
/* reset the TxDL's private */
|
||
txdl_priv->align_dma_offset = 0;
|
||
txdl_priv->align_vaddr_start = txdl_priv->align_vaddr;
|
||
txdl_priv->align_used_frags = 0;
|
||
txdl_priv->frags = 0;
|
||
txdl_priv->alloc_frags =
|
||
((xge_hal_fifo_t *)channelh)->config->max_frags;
|
||
txdl_priv->dang_txdl = NULL;
|
||
txdl_priv->dang_frags = 0;
|
||
txdl_priv->next_txdl_priv = NULL;
|
||
txdl_priv->bytes_sent = 0;
|
||
|
||
/* reset TxD0 */
|
||
txdp->control_1 = txdp->control_2 = 0;
|
||
|
||
#if defined(XGE_OS_MEMORY_CHECK)
|
||
txdl_priv->allocated = 1;
|
||
#endif
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_reserve_sp - Reserve fifo descriptor and store it in
|
||
* the ULD-provided "scratch" memory.
|
||
* @channelh: Channel handle.
|
||
* @dtr_sp_size: Size of the %dtr_sp "scratch pad" that HAL can use for TxDL.
|
||
* @dtr_sp: "Scratch pad" supplied by upper-layer driver (ULD).
|
||
*
|
||
* Reserve TxDL and fill-in ULD supplied "scratch pad". The difference
|
||
* between this API and xge_hal_fifo_dtr_reserve() is (possibly) -
|
||
* performance.
|
||
*
|
||
* If upper-layer uses ULP-defined commands, and if those commands have enough
|
||
* space for HAL/Xframe descriptors - tnan it is better (read: faster) to fit
|
||
* all the per-command information into one command, which is typically
|
||
* one contiguous block.
|
||
*
|
||
* Note: Unlike xge_hal_fifo_dtr_reserve(), this function can be used to
|
||
* allocate a single descriptor for transmit operation.
|
||
*
|
||
* See also: xge_hal_fifo_dtr_reserve(), xge_hal_fifo_dtr_free(),
|
||
* xge_hal_ring_dtr_reserve(), xge_hal_status_e{}.
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e
|
||
xge_hal_fifo_dtr_reserve_sp(xge_hal_channel_h channelh, int dtr_sp_size,
|
||
xge_hal_dtr_h dtr_sp)
|
||
{
|
||
/* FIXME: implement */
|
||
return XGE_HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_post - Post descriptor on the fifo channel.
|
||
* @channelh: Channel handle.
|
||
* @dtrh: Descriptor obtained via xge_hal_fifo_dtr_reserve() or
|
||
* xge_hal_fifo_dtr_reserve_sp()
|
||
* @frags: Number of contiguous buffers that are part of a single
|
||
* transmit operation.
|
||
*
|
||
* Post descriptor on the 'fifo' type channel for transmission.
|
||
* Prior to posting the descriptor should be filled in accordance with
|
||
* Host/Xframe interface specification for a given service (LL, etc.).
|
||
*
|
||
* See also: xge_hal_fifo_dtr_post_many(), xge_hal_ring_dtr_post().
|
||
* Usage: See ex_xmit{}.
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void
|
||
xge_hal_fifo_dtr_post(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh)
|
||
{
|
||
xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh;
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv;
|
||
xge_hal_fifo_txd_t *txdp_last;
|
||
xge_hal_fifo_txd_t *txdp_first;
|
||
#if defined(XGE_HAL_TX_MULTI_POST_IRQ)
|
||
unsigned long flags = 0;
|
||
#endif
|
||
|
||
txdl_priv = __hal_fifo_txdl_priv(dtrh);
|
||
|
||
txdp_first = (xge_hal_fifo_txd_t *)dtrh;
|
||
txdp_first->control_1 |= XGE_HAL_TXD_GATHER_CODE_FIRST;
|
||
txdp_first->control_2 |= fifo->interrupt_type;
|
||
|
||
txdp_last = (xge_hal_fifo_txd_t *)dtrh + (txdl_priv->frags - 1);
|
||
txdp_last->control_1 |= XGE_HAL_TXD_GATHER_CODE_LAST;
|
||
|
||
#if defined(XGE_HAL_TX_MULTI_POST)
|
||
xge_os_spin_lock(fifo->post_lock_ptr);
|
||
#elif defined(XGE_HAL_TX_MULTI_POST_IRQ)
|
||
xge_os_spin_lock_irq(fifo->post_lock_ptr, flags);
|
||
#endif
|
||
|
||
__hal_fifo_dtr_post_single(channelh, dtrh,
|
||
(u64)(XGE_HAL_TX_FIFO_FIRST_LIST | XGE_HAL_TX_FIFO_LAST_LIST));
|
||
|
||
#if defined(XGE_HAL_TX_MULTI_POST)
|
||
xge_os_spin_unlock(fifo->post_lock_ptr);
|
||
#elif defined(XGE_HAL_TX_MULTI_POST_IRQ)
|
||
xge_os_spin_unlock_irq(fifo->post_lock_ptr, flags);
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_post_many - Post multiple descriptors on fifo
|
||
* channel.
|
||
* @channelh: Channel to post descriptor.
|
||
* @num: Number of descriptors (i.e., fifo TxDLs) in the %dtrs[].
|
||
* @dtrs: Descriptors obtained via xge_hal_fifo_dtr_reserve().
|
||
* @frags_arr: Number of fragments carried @dtrs descriptors.
|
||
* Note that frag_arr[i] corresponds to descriptor dtrs[i].
|
||
*
|
||
* Post multi-descriptor on the fifo channel. The operation is atomic:
|
||
* all descriptrs are posted on the channel "back-to-back' without
|
||
* letting other posts (possibly driven by multiple transmitting threads)
|
||
* to interleave.
|
||
*
|
||
* See also: xge_hal_fifo_dtr_post(), xge_hal_ring_dtr_post().
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void
|
||
xge_hal_fifo_dtr_post_many(xge_hal_channel_h channelh, int num,
|
||
xge_hal_dtr_h dtrs[])
|
||
{
|
||
int i;
|
||
xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh;
|
||
xge_hal_fifo_txd_t *txdp_last;
|
||
xge_hal_fifo_txd_t *txdp_first;
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv_last;
|
||
#if defined(XGE_HAL_TX_MULTI_POST_IRQ)
|
||
unsigned long flags = 0;
|
||
#endif
|
||
|
||
xge_assert(num > 1);
|
||
|
||
txdp_first = (xge_hal_fifo_txd_t *)dtrs[0];
|
||
txdp_first->control_1 |= XGE_HAL_TXD_GATHER_CODE_FIRST;
|
||
txdp_first->control_2 |= fifo->interrupt_type;
|
||
|
||
txdl_priv_last = __hal_fifo_txdl_priv(dtrs[num-1]);
|
||
txdp_last = (xge_hal_fifo_txd_t *)dtrs[num-1] +
|
||
(txdl_priv_last->frags - 1);
|
||
txdp_last->control_1 |= XGE_HAL_TXD_GATHER_CODE_LAST;
|
||
|
||
#if defined(XGE_HAL_TX_MULTI_POST)
|
||
xge_os_spin_lock(&((xge_hal_channel_t*)channelh)->post_lock);
|
||
#elif defined(XGE_HAL_TX_MULTI_POST_IRQ)
|
||
xge_os_spin_lock_irq(&((xge_hal_channel_t*)channelh)->post_lock,
|
||
flags);
|
||
#endif
|
||
|
||
for (i=0; i<num; i++) {
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv;
|
||
u64 val64;
|
||
xge_hal_dtr_h dtrh = dtrs[i];
|
||
|
||
txdl_priv = __hal_fifo_txdl_priv(dtrh);
|
||
txdl_priv = txdl_priv; /* Cheat lint */
|
||
|
||
val64 = 0;
|
||
if (i == 0) {
|
||
val64 |= XGE_HAL_TX_FIFO_FIRST_LIST;
|
||
} else if (i == num -1) {
|
||
val64 |= XGE_HAL_TX_FIFO_LAST_LIST;
|
||
}
|
||
|
||
val64 |= XGE_HAL_TX_FIFO_SPECIAL_FUNC;
|
||
__hal_fifo_dtr_post_single(channelh, dtrh, val64);
|
||
}
|
||
|
||
#if defined(XGE_HAL_TX_MULTI_POST)
|
||
xge_os_spin_unlock(&((xge_hal_channel_t*)channelh)->post_lock);
|
||
#elif defined(XGE_HAL_TX_MULTI_POST_IRQ)
|
||
xge_os_spin_unlock_irq(&((xge_hal_channel_t*)channelh)->post_lock,
|
||
flags);
|
||
#endif
|
||
|
||
fifo->channel.stats.total_posts_many++;
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_next_completed - Retrieve next completed descriptor.
|
||
* @channelh: Channel handle.
|
||
* @dtrh: Descriptor handle. Returned by HAL.
|
||
* @t_code: Transfer code, as per Xframe User Guide,
|
||
* Transmit Descriptor Format.
|
||
* Returned by HAL.
|
||
*
|
||
* Retrieve the _next_ completed descriptor.
|
||
* HAL uses channel callback (*xge_hal_channel_callback_f) to notifiy
|
||
* upper-layer driver (ULD) of new completed descriptors. After that
|
||
* the ULD can use xge_hal_fifo_dtr_next_completed to retrieve the rest
|
||
* completions (the very first completion is passed by HAL via
|
||
* xge_hal_channel_callback_f).
|
||
*
|
||
* Implementation-wise, the upper-layer driver is free to call
|
||
* xge_hal_fifo_dtr_next_completed either immediately from inside the
|
||
* channel callback, or in a deferred fashion and separate (from HAL)
|
||
* context.
|
||
*
|
||
* Non-zero @t_code means failure to process the descriptor.
|
||
* The failure could happen, for instance, when the link is
|
||
* down, in which case Xframe completes the descriptor because it
|
||
* is not able to send the data out.
|
||
*
|
||
* For details please refer to Xframe User Guide.
|
||
*
|
||
* Returns: XGE_HAL_OK - success.
|
||
* XGE_HAL_INF_NO_MORE_COMPLETED_DESCRIPTORS - No completed descriptors
|
||
* are currently available for processing.
|
||
*
|
||
* See also: xge_hal_channel_callback_f{},
|
||
* xge_hal_ring_dtr_next_completed().
|
||
* Usage: See ex_tx_compl{}.
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e
|
||
xge_hal_fifo_dtr_next_completed(xge_hal_channel_h channelh,
|
||
xge_hal_dtr_h *dtrh, u8 *t_code)
|
||
{
|
||
xge_hal_fifo_txd_t *txdp;
|
||
xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh;
|
||
#if defined(XGE_OS_DMA_REQUIRES_SYNC) && defined(XGE_HAL_DMA_DTR_STREAMING)
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv;
|
||
#endif
|
||
|
||
__hal_channel_dtr_try_complete(channelh, dtrh);
|
||
txdp = (xge_hal_fifo_txd_t *)*dtrh;
|
||
if (txdp == NULL) {
|
||
return XGE_HAL_INF_NO_MORE_COMPLETED_DESCRIPTORS;
|
||
}
|
||
|
||
#if defined(XGE_OS_DMA_REQUIRES_SYNC) && defined(XGE_HAL_DMA_DTR_STREAMING)
|
||
txdl_priv = __hal_fifo_txdl_priv(txdp);
|
||
|
||
/* sync TxDL to read the ownership
|
||
*
|
||
* Note: 16bytes means Control_1 & Control_2 */
|
||
xge_os_dma_sync(fifo->channel.pdev,
|
||
txdl_priv->dma_handle,
|
||
txdl_priv->dma_addr,
|
||
txdl_priv->dma_offset,
|
||
16,
|
||
XGE_OS_DMA_DIR_FROMDEVICE);
|
||
#endif
|
||
|
||
/* check whether host owns it */
|
||
if ( !(txdp->control_1 & XGE_HAL_TXD_LIST_OWN_XENA) ) {
|
||
|
||
xge_assert(txdp->host_control!=0);
|
||
|
||
__hal_channel_dtr_complete(channelh);
|
||
|
||
*t_code = (u8)XGE_HAL_GET_TXD_T_CODE(txdp->control_1);
|
||
|
||
/* see XGE_HAL_SET_TXD_T_CODE() above.. */
|
||
xge_assert(*t_code != XGE_HAL_TXD_T_CODE_UNUSED_5);
|
||
|
||
if (fifo->channel.usage_cnt > 0)
|
||
fifo->channel.usage_cnt--;
|
||
|
||
return XGE_HAL_OK;
|
||
}
|
||
|
||
/* no more completions */
|
||
*dtrh = 0;
|
||
return XGE_HAL_INF_NO_MORE_COMPLETED_DESCRIPTORS;
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_free - Free descriptor.
|
||
* @channelh: Channel handle.
|
||
* @dtr: Descriptor handle.
|
||
*
|
||
* Free the reserved descriptor. This operation is "symmetrical" to
|
||
* xge_hal_fifo_dtr_reserve or xge_hal_fifo_dtr_reserve_sp.
|
||
* The "free-ing" completes the descriptor's lifecycle.
|
||
*
|
||
* After free-ing (see xge_hal_fifo_dtr_free()) the descriptor again can
|
||
* be:
|
||
*
|
||
* - reserved (xge_hal_fifo_dtr_reserve);
|
||
*
|
||
* - posted (xge_hal_fifo_dtr_post);
|
||
*
|
||
* - completed (xge_hal_fifo_dtr_next_completed);
|
||
*
|
||
* - and recycled again (xge_hal_fifo_dtr_free).
|
||
*
|
||
* For alternative state transitions and more details please refer to
|
||
* the design doc.
|
||
*
|
||
* See also: xge_hal_ring_dtr_free(), xge_hal_fifo_dtr_reserve().
|
||
* Usage: See ex_tx_compl{}.
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void
|
||
xge_hal_fifo_dtr_free(xge_hal_channel_h channelh, xge_hal_dtr_h dtr)
|
||
{
|
||
#if defined(XGE_HAL_TX_MULTI_FREE_IRQ)
|
||
unsigned long flags = 0;
|
||
#endif
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv = __hal_fifo_txdl_priv(
|
||
(xge_hal_fifo_txd_t *)dtr);
|
||
int max_frags = ((xge_hal_fifo_t *)channelh)->config->max_frags;
|
||
#if defined(XGE_HAL_TX_MULTI_FREE)
|
||
xge_os_spin_lock(&((xge_hal_channel_t*)channelh)->free_lock);
|
||
#elif defined(XGE_HAL_TX_MULTI_FREE_IRQ)
|
||
xge_os_spin_lock_irq(&((xge_hal_channel_t*)channelh)->free_lock,
|
||
flags);
|
||
#endif
|
||
|
||
if (txdl_priv->alloc_frags > max_frags) {
|
||
xge_hal_fifo_txd_t *dang_txdp = (xge_hal_fifo_txd_t *)
|
||
txdl_priv->dang_txdl;
|
||
int dang_frags = txdl_priv->dang_frags;
|
||
int alloc_frags = txdl_priv->alloc_frags;
|
||
txdl_priv->dang_txdl = NULL;
|
||
txdl_priv->dang_frags = 0;
|
||
txdl_priv->alloc_frags = 0;
|
||
/* dtrh must have a linked list of dtrh */
|
||
xge_assert(txdl_priv->next_txdl_priv);
|
||
|
||
/* free any dangling dtrh first */
|
||
if (dang_txdp) {
|
||
xge_debug_fifo(XGE_TRACE,
|
||
"freeing dangled dtrh %p for %d fragments",
|
||
dang_txdp, dang_frags);
|
||
__hal_fifo_txdl_free_many(channelh, dang_txdp,
|
||
max_frags, dang_frags);
|
||
}
|
||
|
||
/* now free the reserved dtrh list */
|
||
xge_debug_fifo(XGE_TRACE,
|
||
"freeing dtrh %p list of %d fragments", dtr,
|
||
alloc_frags);
|
||
__hal_fifo_txdl_free_many(channelh,
|
||
(xge_hal_fifo_txd_t *)dtr, max_frags,
|
||
alloc_frags);
|
||
}
|
||
else
|
||
__hal_channel_dtr_free(channelh, dtr);
|
||
|
||
((xge_hal_channel_t *)channelh)->poll_bytes += txdl_priv->bytes_sent;
|
||
|
||
#if defined(XGE_DEBUG_ASSERT) && defined(XGE_OS_MEMORY_CHECK)
|
||
__hal_fifo_txdl_priv(dtr)->allocated = 0;
|
||
#endif
|
||
|
||
#if defined(XGE_HAL_TX_MULTI_FREE)
|
||
xge_os_spin_unlock(&((xge_hal_channel_t*)channelh)->free_lock);
|
||
#elif defined(XGE_HAL_TX_MULTI_FREE_IRQ)
|
||
xge_os_spin_unlock_irq(&((xge_hal_channel_t*)channelh)->free_lock,
|
||
flags);
|
||
#endif
|
||
}
|
||
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_buffer_set_aligned - Align transmit buffer and fill
|
||
* in fifo descriptor.
|
||
* @channelh: Channel handle.
|
||
* @dtrh: Descriptor handle.
|
||
* @frag_idx: Index of the data buffer in the caller's scatter-gather list<73>
|
||
* (of buffers).
|
||
* @vaddr: Virtual address of the data buffer.
|
||
* @dma_pointer: DMA address of the data buffer referenced by @frag_idx.
|
||
* @size: Size of the data buffer (in bytes).
|
||
* @misaligned_size: Size (in bytes) of the misaligned portion of the
|
||
* data buffer. Calculated by the caller, based on the platform/OS/other
|
||
* specific criteria, which is outside of HAL's domain. See notes below.
|
||
*
|
||
* This API is part of the transmit descriptor preparation for posting
|
||
* (via xge_hal_fifo_dtr_post()). The related "preparation" APIs include
|
||
* xge_hal_fifo_dtr_mss_set() and xge_hal_fifo_dtr_cksum_set_bits().
|
||
* All three APIs fill in the fields of the fifo descriptor,
|
||
* in accordance with the Xframe specification.
|
||
* On the PCI-X based systems aligning transmit data typically provides better
|
||
* transmit performance. The typical alignment granularity: L2 cacheline size.
|
||
* However, HAL does not make assumptions in terms of the alignment granularity;
|
||
* this is specified via additional @misaligned_size parameter described above.
|
||
* Prior to calling xge_hal_fifo_dtr_buffer_set_aligned(),
|
||
* ULD is supposed to check alignment of a given fragment/buffer. For this HAL
|
||
* provides a separate xge_hal_check_alignment() API sufficient to cover
|
||
* most (but not all) possible alignment criteria.
|
||
* If the buffer appears to be aligned, the ULD calls
|
||
* xge_hal_fifo_dtr_buffer_set().
|
||
* Otherwise, ULD calls xge_hal_fifo_dtr_buffer_set_aligned().
|
||
*
|
||
* Note; This API is a "superset" of xge_hal_fifo_dtr_buffer_set(). In
|
||
* addition to filling in the specified descriptor it aligns transmit data on
|
||
* the specified boundary.
|
||
* Note: Decision on whether to align or not to align a given contiguous
|
||
* transmit buffer is outside of HAL's domain. To this end ULD can use any
|
||
* programmable criteria, which can help to 1) boost transmit performance,
|
||
* and/or 2) provide a workaround for PCI bridge bugs, if any.
|
||
*
|
||
* See also: xge_hal_fifo_dtr_buffer_set(),
|
||
* xge_hal_check_alignment().
|
||
*
|
||
* See also: xge_hal_fifo_dtr_reserve(), xge_hal_fifo_dtr_post(),
|
||
* xge_hal_fifo_dtr_mss_set(), xge_hal_fifo_dtr_cksum_set_bits()
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e
|
||
xge_hal_fifo_dtr_buffer_set_aligned(xge_hal_channel_h channelh,
|
||
xge_hal_dtr_h dtrh, int frag_idx, void *vaddr,
|
||
dma_addr_t dma_pointer, int size, int misaligned_size)
|
||
{
|
||
xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh;
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv;
|
||
xge_hal_fifo_txd_t *txdp;
|
||
int remaining_size;
|
||
ptrdiff_t prev_boff;
|
||
|
||
txdl_priv = __hal_fifo_txdl_priv(dtrh);
|
||
txdp = (xge_hal_fifo_txd_t *)dtrh + txdl_priv->frags;
|
||
|
||
if (frag_idx != 0) {
|
||
txdp->control_1 = txdp->control_2 = 0;
|
||
}
|
||
|
||
/* On some systems buffer size could be zero.
|
||
* It is the responsibility of ULD and *not HAL* to
|
||
* detect it and skip it. */
|
||
xge_assert(size > 0);
|
||
xge_assert(frag_idx < txdl_priv->alloc_frags);
|
||
xge_assert(misaligned_size != 0 &&
|
||
misaligned_size <= fifo->config->alignment_size);
|
||
|
||
remaining_size = size - misaligned_size;
|
||
xge_assert(remaining_size >= 0);
|
||
|
||
xge_os_memcpy((char*)txdl_priv->align_vaddr_start,
|
||
vaddr, misaligned_size);
|
||
|
||
if (txdl_priv->align_used_frags >= fifo->config->max_aligned_frags) {
|
||
return XGE_HAL_ERR_OUT_ALIGNED_FRAGS;
|
||
}
|
||
|
||
/* setup new buffer */
|
||
prev_boff = txdl_priv->align_vaddr_start - txdl_priv->align_vaddr;
|
||
txdp->buffer_pointer = (u64)txdl_priv->align_dma_addr + prev_boff;
|
||
txdp->control_1 |= XGE_HAL_TXD_BUFFER0_SIZE(misaligned_size);
|
||
txdl_priv->bytes_sent += misaligned_size;
|
||
fifo->channel.stats.total_buffers++;
|
||
txdl_priv->frags++;
|
||
txdl_priv->align_used_frags++;
|
||
txdl_priv->align_vaddr_start += fifo->config->alignment_size;
|
||
txdl_priv->align_dma_offset = 0;
|
||
|
||
#if defined(XGE_OS_DMA_REQUIRES_SYNC)
|
||
/* sync new buffer */
|
||
xge_os_dma_sync(fifo->channel.pdev,
|
||
txdl_priv->align_dma_handle,
|
||
txdp->buffer_pointer,
|
||
0,
|
||
misaligned_size,
|
||
XGE_OS_DMA_DIR_TODEVICE);
|
||
#endif
|
||
|
||
if (remaining_size) {
|
||
xge_assert(frag_idx < txdl_priv->alloc_frags);
|
||
txdp++;
|
||
txdp->buffer_pointer = (u64)dma_pointer +
|
||
misaligned_size;
|
||
txdp->control_1 =
|
||
XGE_HAL_TXD_BUFFER0_SIZE(remaining_size);
|
||
txdl_priv->bytes_sent += remaining_size;
|
||
txdp->control_2 = 0;
|
||
fifo->channel.stats.total_buffers++;
|
||
txdl_priv->frags++;
|
||
}
|
||
|
||
return XGE_HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_buffer_append - Append the contents of virtually
|
||
* contiguous data buffer to a single physically contiguous buffer.
|
||
* @channelh: Channel handle.
|
||
* @dtrh: Descriptor handle.
|
||
* @vaddr: Virtual address of the data buffer.
|
||
* @size: Size of the data buffer (in bytes).
|
||
*
|
||
* This API is part of the transmit descriptor preparation for posting
|
||
* (via xge_hal_fifo_dtr_post()).
|
||
* The main difference of this API wrt to the APIs
|
||
* xge_hal_fifo_dtr_buffer_set_aligned() is that this API appends the
|
||
* contents of virtually contiguous data buffers received from
|
||
* upper layer into a single physically contiguous data buffer and the
|
||
* device will do a DMA from this buffer.
|
||
*
|
||
* See Also: xge_hal_fifo_dtr_buffer_finalize(), xge_hal_fifo_dtr_buffer_set(),
|
||
* xge_hal_fifo_dtr_buffer_set_aligned().
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e
|
||
xge_hal_fifo_dtr_buffer_append(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh,
|
||
void *vaddr, int size)
|
||
{
|
||
xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh;
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv;
|
||
ptrdiff_t used;
|
||
|
||
xge_assert(size > 0);
|
||
|
||
txdl_priv = __hal_fifo_txdl_priv(dtrh);
|
||
|
||
used = txdl_priv->align_vaddr_start - txdl_priv->align_vaddr;
|
||
used += txdl_priv->align_dma_offset;
|
||
if (used + (unsigned int)size > (unsigned int)fifo->align_size)
|
||
return XGE_HAL_ERR_OUT_ALIGNED_FRAGS;
|
||
|
||
xge_os_memcpy((char*)txdl_priv->align_vaddr_start +
|
||
txdl_priv->align_dma_offset, vaddr, size);
|
||
|
||
fifo->channel.stats.copied_frags++;
|
||
|
||
txdl_priv->align_dma_offset += size;
|
||
return XGE_HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_buffer_finalize - Prepares a descriptor that contains the
|
||
* single physically contiguous buffer.
|
||
*
|
||
* @channelh: Channel handle.
|
||
* @dtrh: Descriptor handle.
|
||
* @frag_idx: Index of the data buffer in the Txdl list.
|
||
*
|
||
* This API in conjuction with xge_hal_fifo_dtr_buffer_append() prepares
|
||
* a descriptor that consists of a single physically contiguous buffer
|
||
* which inturn contains the contents of one or more virtually contiguous
|
||
* buffers received from the upper layer.
|
||
*
|
||
* See Also: xge_hal_fifo_dtr_buffer_append().
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void
|
||
xge_hal_fifo_dtr_buffer_finalize(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh,
|
||
int frag_idx)
|
||
{
|
||
xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh;
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv;
|
||
xge_hal_fifo_txd_t *txdp;
|
||
ptrdiff_t prev_boff;
|
||
|
||
xge_assert(frag_idx < fifo->config->max_frags);
|
||
|
||
txdl_priv = __hal_fifo_txdl_priv(dtrh);
|
||
txdp = (xge_hal_fifo_txd_t *)dtrh + txdl_priv->frags;
|
||
|
||
if (frag_idx != 0) {
|
||
txdp->control_1 = txdp->control_2 = 0;
|
||
}
|
||
|
||
prev_boff = txdl_priv->align_vaddr_start - txdl_priv->align_vaddr;
|
||
txdp->buffer_pointer = (u64)txdl_priv->align_dma_addr + prev_boff;
|
||
txdp->control_1 |=
|
||
XGE_HAL_TXD_BUFFER0_SIZE(txdl_priv->align_dma_offset);
|
||
txdl_priv->bytes_sent += (unsigned int)txdl_priv->align_dma_offset;
|
||
fifo->channel.stats.total_buffers++;
|
||
fifo->channel.stats.copied_buffers++;
|
||
txdl_priv->frags++;
|
||
txdl_priv->align_used_frags++;
|
||
|
||
#if defined(XGE_OS_DMA_REQUIRES_SYNC)
|
||
/* sync pre-mapped buffer */
|
||
xge_os_dma_sync(fifo->channel.pdev,
|
||
txdl_priv->align_dma_handle,
|
||
txdp->buffer_pointer,
|
||
0,
|
||
txdl_priv->align_dma_offset,
|
||
XGE_OS_DMA_DIR_TODEVICE);
|
||
#endif
|
||
|
||
/* increment vaddr_start for the next buffer_append() iteration */
|
||
txdl_priv->align_vaddr_start += txdl_priv->align_dma_offset;
|
||
txdl_priv->align_dma_offset = 0;
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_buffer_set - Set transmit buffer pointer in the
|
||
* descriptor.
|
||
* @channelh: Channel handle.
|
||
* @dtrh: Descriptor handle.
|
||
* @frag_idx: Index of the data buffer in the caller's scatter-gather list<73>
|
||
* (of buffers).
|
||
* @dma_pointer: DMA address of the data buffer referenced by @frag_idx.
|
||
* @size: Size of the data buffer (in bytes).
|
||
*
|
||
* This API is part of the preparation of the transmit descriptor for posting
|
||
* (via xge_hal_fifo_dtr_post()). The related "preparation" APIs include
|
||
* xge_hal_fifo_dtr_mss_set() and xge_hal_fifo_dtr_cksum_set_bits().
|
||
* All three APIs fill in the fields of the fifo descriptor,
|
||
* in accordance with the Xframe specification.
|
||
*
|
||
* See also: xge_hal_fifo_dtr_buffer_set_aligned(),
|
||
* xge_hal_check_alignment().
|
||
*
|
||
* See also: xge_hal_fifo_dtr_reserve(), xge_hal_fifo_dtr_post(),
|
||
* xge_hal_fifo_dtr_mss_set(), xge_hal_fifo_dtr_cksum_set_bits()
|
||
* Prepare transmit descriptor for transmission (via
|
||
* xge_hal_fifo_dtr_post()).
|
||
* See also: xge_hal_fifo_dtr_vlan_set().
|
||
* Note: Compare with xge_hal_fifo_dtr_buffer_set_aligned().
|
||
*
|
||
* Usage: See ex_xmit{}.
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void
|
||
xge_hal_fifo_dtr_buffer_set(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh,
|
||
int frag_idx, dma_addr_t dma_pointer, int size)
|
||
{
|
||
xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh;
|
||
xge_hal_fifo_txdl_priv_t *txdl_priv;
|
||
xge_hal_fifo_txd_t *txdp;
|
||
|
||
txdl_priv = __hal_fifo_txdl_priv(dtrh);
|
||
txdp = (xge_hal_fifo_txd_t *)dtrh + txdl_priv->frags;
|
||
|
||
if (frag_idx != 0) {
|
||
txdp->control_1 = txdp->control_2 = 0;
|
||
}
|
||
|
||
/* Note:
|
||
* it is the responsibility of upper layers and not HAL
|
||
* detect it and skip zero-size fragment
|
||
*/
|
||
xge_assert(size > 0);
|
||
xge_assert(frag_idx < txdl_priv->alloc_frags);
|
||
|
||
txdp->buffer_pointer = (u64)dma_pointer;
|
||
txdp->control_1 |= XGE_HAL_TXD_BUFFER0_SIZE(size);
|
||
txdl_priv->bytes_sent += size;
|
||
fifo->channel.stats.total_buffers++;
|
||
txdl_priv->frags++;
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_mss_set - Set MSS.
|
||
* @dtrh: Descriptor handle.
|
||
* @mss: MSS size for _this_ TCP connection. Passed by TCP stack down to the
|
||
* ULD, which in turn inserts the MSS into the @dtrh.
|
||
*
|
||
* This API is part of the preparation of the transmit descriptor for posting
|
||
* (via xge_hal_fifo_dtr_post()). The related "preparation" APIs include
|
||
* xge_hal_fifo_dtr_buffer_set(), xge_hal_fifo_dtr_buffer_set_aligned(),
|
||
* and xge_hal_fifo_dtr_cksum_set_bits().
|
||
* All these APIs fill in the fields of the fifo descriptor,
|
||
* in accordance with the Xframe specification.
|
||
*
|
||
* See also: xge_hal_fifo_dtr_reserve(),
|
||
* xge_hal_fifo_dtr_post(), xge_hal_fifo_dtr_vlan_set().
|
||
* Usage: See ex_xmit{}.
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void
|
||
xge_hal_fifo_dtr_mss_set(xge_hal_dtr_h dtrh, int mss)
|
||
{
|
||
xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)dtrh;
|
||
|
||
txdp->control_1 |= XGE_HAL_TXD_LSO_COF_CTRL(XGE_HAL_TXD_TCP_LSO);
|
||
txdp->control_1 |= XGE_HAL_TXD_TCP_LSO_MSS(mss);
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_cksum_set_bits - Offload checksum.
|
||
* @dtrh: Descriptor handle.
|
||
* @cksum_bits: Specifies which checksums are to be offloaded: IPv4,
|
||
* and/or TCP and/or UDP.
|
||
*
|
||
* Ask Xframe to calculate IPv4 & transport checksums for _this_ transmit
|
||
* descriptor.
|
||
* This API is part of the preparation of the transmit descriptor for posting
|
||
* (via xge_hal_fifo_dtr_post()). The related "preparation" APIs include
|
||
* xge_hal_fifo_dtr_mss_set(), xge_hal_fifo_dtr_buffer_set_aligned(),
|
||
* and xge_hal_fifo_dtr_buffer_set().
|
||
* All these APIs fill in the fields of the fifo descriptor,
|
||
* in accordance with the Xframe specification.
|
||
*
|
||
* See also: xge_hal_fifo_dtr_reserve(),
|
||
* xge_hal_fifo_dtr_post(), XGE_HAL_TXD_TX_CKO_IPV4_EN,
|
||
* XGE_HAL_TXD_TX_CKO_TCP_EN.
|
||
* Usage: See ex_xmit{}.
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void
|
||
xge_hal_fifo_dtr_cksum_set_bits(xge_hal_dtr_h dtrh, u64 cksum_bits)
|
||
{
|
||
xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)dtrh;
|
||
|
||
txdp->control_2 |= cksum_bits;
|
||
}
|
||
|
||
|
||
/**
|
||
* xge_hal_fifo_dtr_vlan_set - Set VLAN tag.
|
||
* @dtrh: Descriptor handle.
|
||
* @vlan_tag: 16bit VLAN tag.
|
||
*
|
||
* Insert VLAN tag into specified transmit descriptor.
|
||
* The actual insertion of the tag into outgoing frame is done by the hardware.
|
||
* See also: xge_hal_fifo_dtr_buffer_set(), xge_hal_fifo_dtr_mss_set().
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO void
|
||
xge_hal_fifo_dtr_vlan_set(xge_hal_dtr_h dtrh, u16 vlan_tag)
|
||
{
|
||
xge_hal_fifo_txd_t *txdp = (xge_hal_fifo_txd_t *)dtrh;
|
||
|
||
txdp->control_2 |= XGE_HAL_TXD_VLAN_ENABLE;
|
||
txdp->control_2 |= XGE_HAL_TXD_VLAN_TAG(vlan_tag);
|
||
}
|
||
|
||
/**
|
||
* xge_hal_fifo_is_next_dtr_completed - Checks if the next dtr is completed
|
||
* @channelh: Channel handle.
|
||
*/
|
||
__HAL_STATIC_FIFO __HAL_INLINE_FIFO xge_hal_status_e
|
||
xge_hal_fifo_is_next_dtr_completed(xge_hal_channel_h channelh)
|
||
{
|
||
xge_hal_fifo_txd_t *txdp;
|
||
xge_hal_dtr_h dtrh;
|
||
|
||
__hal_channel_dtr_try_complete(channelh, &dtrh);
|
||
txdp = (xge_hal_fifo_txd_t *)dtrh;
|
||
if (txdp == NULL) {
|
||
return XGE_HAL_INF_NO_MORE_COMPLETED_DESCRIPTORS;
|
||
}
|
||
|
||
/* check whether host owns it */
|
||
if ( !(txdp->control_1 & XGE_HAL_TXD_LIST_OWN_XENA) ) {
|
||
xge_assert(txdp->host_control!=0);
|
||
return XGE_HAL_OK;
|
||
}
|
||
|
||
/* no more completions */
|
||
return XGE_HAL_INF_NO_MORE_COMPLETED_DESCRIPTORS;
|
||
}
|