709 lines
17 KiB
C
709 lines
17 KiB
C
|
/*! \file bcmcnet_rxtx.c
|
||
|
*
|
||
|
* Utility routines for BCMCNET Rx/Tx.
|
||
|
*
|
||
|
*/
|
||
|
/*
|
||
|
* $Copyright: Copyright 2018-2020 Broadcom. All rights reserved.
|
||
|
* The term 'Broadcom' refers to Broadcom Inc. and/or its subsidiaries.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public License
|
||
|
* version 2 as published by the Free Software Foundation.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* A copy of the GNU General Public License version 2 (GPLv2) can
|
||
|
* be found in the LICENSES folder.$
|
||
|
*/
|
||
|
|
||
|
#include <bcmcnet/bcmcnet_core.h>
|
||
|
#include <bcmcnet/bcmcnet_dev.h>
|
||
|
#include <bcmcnet/bcmcnet_rxtx.h>
|
||
|
#include <bcmcnet/bcmcnet_buff.h>
|
||
|
|
||
|
/*!
|
||
|
* Free a Rx ring
|
||
|
*/
|
||
|
static void
|
||
|
bcn_rx_ring_free(struct pdma_rx_queue *rxq)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = rxq->ctrl;
|
||
|
struct pdma_buf_mngr *bm = (struct pdma_buf_mngr *)ctrl->buf_mngr;
|
||
|
|
||
|
if (rxq->lock) {
|
||
|
sal_spinlock_destroy(rxq->lock);
|
||
|
rxq->lock = NULL;
|
||
|
}
|
||
|
|
||
|
if (rxq->ring) {
|
||
|
bm->ring_buf_free(ctrl->dev, ctrl->rx_desc_size * (rxq->nb_desc + 1),
|
||
|
rxq->ring, rxq->ring_addr);
|
||
|
rxq->ring = NULL;
|
||
|
}
|
||
|
|
||
|
if (rxq->pbuf) {
|
||
|
sal_free(rxq->pbuf);
|
||
|
rxq->pbuf = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Allocate a Rx ring
|
||
|
*/
|
||
|
static int
|
||
|
bcn_rx_ring_alloc(struct pdma_rx_queue *rxq)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = rxq->ctrl;
|
||
|
struct pdma_buf_mngr *bm = (struct pdma_buf_mngr *)ctrl->buf_mngr;
|
||
|
|
||
|
/* Setup pktbuf ring */
|
||
|
rxq->pbuf = sal_alloc(sizeof(*rxq->pbuf) * rxq->nb_desc, "bcmcnetRxBufRing");
|
||
|
if (!rxq->pbuf) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
sal_memset(rxq->pbuf, 0, sizeof(*rxq->pbuf) * rxq->nb_desc);
|
||
|
|
||
|
/* Allocate memory for descriptors */
|
||
|
rxq->ring = bm->ring_buf_alloc(ctrl->dev, ctrl->rx_desc_size * (rxq->nb_desc + 1),
|
||
|
&rxq->ring_addr);
|
||
|
if (!rxq->ring) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
sal_memset(rxq->ring, 0, ctrl->rx_desc_size * (rxq->nb_desc + 1));
|
||
|
|
||
|
rxq->lock = sal_spinlock_create("bcmcnetRxQueueLock");
|
||
|
if (!rxq->lock) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
|
||
|
cleanup:
|
||
|
bcn_rx_ring_free(rxq);
|
||
|
|
||
|
return SHR_E_MEMORY;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Free a Tx ring
|
||
|
*/
|
||
|
static void
|
||
|
bcn_tx_ring_free(struct pdma_tx_queue *txq)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = txq->ctrl;
|
||
|
struct pdma_buf_mngr *bm = (struct pdma_buf_mngr *)ctrl->buf_mngr;
|
||
|
|
||
|
if (txq->sem) {
|
||
|
sal_sem_destroy(txq->sem);
|
||
|
txq->sem = NULL;
|
||
|
}
|
||
|
|
||
|
if (txq->mutex) {
|
||
|
sal_spinlock_destroy(txq->mutex);
|
||
|
txq->mutex = NULL;
|
||
|
}
|
||
|
|
||
|
if (txq->lock) {
|
||
|
sal_spinlock_destroy(txq->lock);
|
||
|
txq->lock = NULL;
|
||
|
}
|
||
|
|
||
|
if (txq->ring) {
|
||
|
bm->ring_buf_free(ctrl->dev, ctrl->tx_desc_size * (txq->nb_desc + 1),
|
||
|
txq->ring, txq->ring_addr);
|
||
|
txq->ring = NULL;
|
||
|
}
|
||
|
|
||
|
if (txq->pbuf) {
|
||
|
sal_free(txq->pbuf);
|
||
|
txq->pbuf = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Allocate a Tx ring
|
||
|
*/
|
||
|
static int
|
||
|
bcn_tx_ring_alloc(struct pdma_tx_queue *txq)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = txq->ctrl;
|
||
|
struct pdma_buf_mngr *bm = (struct pdma_buf_mngr *)ctrl->buf_mngr;
|
||
|
|
||
|
/* Setup pktbuf ring */
|
||
|
txq->pbuf = sal_alloc(sizeof(*txq->pbuf) * txq->nb_desc, "bcmcnetTxBufRing");
|
||
|
if (!txq->pbuf) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
sal_memset(txq->pbuf, 0, sizeof(*txq->pbuf) * txq->nb_desc);
|
||
|
|
||
|
/* Allocate memory for descriptors */
|
||
|
txq->ring = bm->ring_buf_alloc(ctrl->dev, ctrl->tx_desc_size * (txq->nb_desc + 1),
|
||
|
&txq->ring_addr);
|
||
|
if (!txq->ring) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
sal_memset(txq->ring, 0, ctrl->tx_desc_size * (txq->nb_desc + 1));
|
||
|
|
||
|
txq->lock = sal_spinlock_create("bcmcnetTxQueueLock");
|
||
|
if (!txq->lock) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
txq->mutex = sal_spinlock_create("bcmcnetTxMutexLock");
|
||
|
if (!txq->mutex) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
txq->sem = sal_sem_create("bcmcnetTxMutexSem", SAL_SEM_BINARY, 0);
|
||
|
if (!txq->sem) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
|
||
|
cleanup:
|
||
|
bcn_tx_ring_free(txq);
|
||
|
|
||
|
return SHR_E_MEMORY;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Rx polling
|
||
|
*/
|
||
|
static int
|
||
|
bcn_rx_poll(struct pdma_rx_queue *rxq, int budget)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = rxq->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
|
||
|
return hw->dops.rx_ring_clean(hw, rxq, budget);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Tx polling
|
||
|
*/
|
||
|
static int
|
||
|
bcn_tx_poll(struct pdma_tx_queue *txq, int budget)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = txq->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
|
||
|
return hw->dops.tx_ring_clean(hw, txq, budget);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Setup a Rx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_rx_queue_setup(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
struct pdma_rx_queue *rxq = NULL;
|
||
|
int rv;
|
||
|
|
||
|
rxq = (struct pdma_rx_queue *)ctrl->rx_queue[queue];
|
||
|
if (rxq->state & PDMA_RX_QUEUE_SETUP) {
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
rv = bcn_rx_ring_alloc(rxq);
|
||
|
if (SHR_FAILURE(rv)) {
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
rv = hw->dops.rx_desc_init(hw, rxq);
|
||
|
if (SHR_FAILURE(rv)) {
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
if (dev->mode == DEV_MODE_VNET) {
|
||
|
ctrl->vsync.rx_ring_addr[queue] = rxq->ring_addr;
|
||
|
ctrl->vsync.rx_ring_size[queue] = rxq->nb_desc;
|
||
|
}
|
||
|
|
||
|
rxq->state |= PDMA_RX_QUEUE_SETUP;
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Release a Rx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_rx_queue_release(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
struct pdma_rx_queue *rxq = NULL;
|
||
|
|
||
|
rxq = (struct pdma_rx_queue *)ctrl->rx_queue[queue];
|
||
|
if (rxq->state & PDMA_RX_QUEUE_SETUP) {
|
||
|
hw->dops.rx_desc_clean(hw, rxq);
|
||
|
bcn_rx_ring_free(rxq);
|
||
|
rxq->state &= ~PDMA_RX_QUEUE_SETUP;
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Restore a Rx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_rx_queue_restore(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
struct pdma_rx_queue *rxq = NULL;
|
||
|
|
||
|
rxq = (struct pdma_rx_queue *)ctrl->rx_queue[queue];
|
||
|
if (rxq->state & PDMA_RX_QUEUE_SETUP) {
|
||
|
hw->dops.rx_desc_init(hw, rxq);
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Set up a virtual Rx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_rx_vqueue_setup(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_rx_queue *vrxq = NULL;
|
||
|
|
||
|
vrxq = (struct pdma_rx_queue *)ctrl->vnet_rxq[queue];
|
||
|
if (vrxq->state & PDMA_RX_QUEUE_SETUP) {
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
if (dev->ctrl.vsync.rx_ring_addr[queue]) {
|
||
|
vrxq->curr = 0;
|
||
|
vrxq->nb_desc = dev->ctrl.vsync.rx_ring_size[queue];
|
||
|
vrxq->ring_addr = dev->ctrl.vsync.rx_ring_addr[queue];
|
||
|
vrxq->ring = dev->sys_p2v(dev, vrxq->ring_addr);
|
||
|
vrxq->state |= PDMA_RX_QUEUE_SETUP;
|
||
|
} else {
|
||
|
return SHR_E_UNAVAIL;
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Release a virtual Rx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_rx_vqueue_release(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_rx_queue *vrxq = NULL;
|
||
|
|
||
|
vrxq = (struct pdma_rx_queue *)ctrl->vnet_rxq[queue];
|
||
|
if (vrxq->state & PDMA_RX_QUEUE_SETUP) {
|
||
|
vrxq->state &= ~PDMA_RX_QUEUE_SETUP;
|
||
|
vrxq->ring = NULL;
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Setup a Tx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_tx_queue_setup(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
struct pdma_tx_queue *txq = NULL;
|
||
|
int rv;
|
||
|
|
||
|
txq = (struct pdma_tx_queue *)ctrl->tx_queue[queue];
|
||
|
if (txq->state & PDMA_TX_QUEUE_SETUP) {
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
rv = bcn_tx_ring_alloc(txq);
|
||
|
if (SHR_FAILURE(rv)) {
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
rv = hw->dops.tx_desc_init(hw, txq);
|
||
|
if (SHR_FAILURE(rv)) {
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
if (dev->mode == DEV_MODE_VNET) {
|
||
|
ctrl->vsync.tx_ring_addr[queue] = txq->ring_addr;
|
||
|
ctrl->vsync.tx_ring_size[queue] = txq->nb_desc;
|
||
|
}
|
||
|
|
||
|
txq->state |= PDMA_TX_QUEUE_SETUP;
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Release a Tx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_tx_queue_release(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
struct pdma_tx_queue *txq = NULL;
|
||
|
|
||
|
txq = (struct pdma_tx_queue *)ctrl->tx_queue[queue];
|
||
|
if (txq->state & PDMA_TX_QUEUE_SETUP) {
|
||
|
hw->dops.tx_desc_clean(hw, txq);
|
||
|
bcn_tx_ring_free(txq);
|
||
|
txq->state &= ~PDMA_TX_QUEUE_SETUP;
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Restore a Tx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_tx_queue_restore(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
struct pdma_tx_queue *txq = NULL;
|
||
|
|
||
|
txq = (struct pdma_tx_queue *)ctrl->tx_queue[queue];
|
||
|
if (txq->state & PDMA_TX_QUEUE_SETUP) {
|
||
|
hw->dops.tx_desc_init(hw, txq);
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Set up a virtual Tx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_tx_vqueue_setup(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_tx_queue *vtxq = NULL;
|
||
|
|
||
|
vtxq = (struct pdma_tx_queue *)ctrl->vnet_txq[queue];
|
||
|
if (vtxq->state & PDMA_TX_QUEUE_SETUP) {
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
if (dev->ctrl.vsync.tx_ring_addr[queue]) {
|
||
|
vtxq->curr = 0;
|
||
|
vtxq->dirt = 0;
|
||
|
vtxq->nb_desc = dev->ctrl.vsync.tx_ring_size[queue];
|
||
|
vtxq->ring_addr = dev->ctrl.vsync.tx_ring_addr[queue];
|
||
|
vtxq->ring = dev->sys_p2v(dev, vtxq->ring_addr);
|
||
|
vtxq->state |= PDMA_TX_QUEUE_SETUP;
|
||
|
} else {
|
||
|
return SHR_E_UNAVAIL;
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Release a virtual Tx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_tx_vqueue_release(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_tx_queue *vtxq = NULL;
|
||
|
|
||
|
vtxq = (struct pdma_tx_queue *)ctrl->vnet_txq[queue];
|
||
|
if (vtxq->state & PDMA_TX_QUEUE_SETUP) {
|
||
|
vtxq->state &= ~PDMA_TX_QUEUE_SETUP;
|
||
|
vtxq->ring = NULL;
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Suspend a Rx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_rx_queue_suspend(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
struct pdma_rx_queue *rxq = NULL;
|
||
|
|
||
|
rxq = (struct pdma_rx_queue *)ctrl->rx_queue[queue];
|
||
|
if (!rxq || !(rxq->state & PDMA_RX_QUEUE_ACTIVE)) {
|
||
|
return SHR_E_UNAVAIL;
|
||
|
}
|
||
|
|
||
|
return hw->dops.rx_suspend(hw, rxq);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Resume a Rx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_rx_queue_resume(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
struct pdma_rx_queue *rxq = NULL;
|
||
|
|
||
|
rxq = (struct pdma_rx_queue *)ctrl->rx_queue[queue];
|
||
|
if (!rxq || !(rxq->state & PDMA_RX_QUEUE_ACTIVE)) {
|
||
|
return SHR_E_UNAVAIL;
|
||
|
}
|
||
|
|
||
|
return hw->dops.rx_resume(hw, rxq);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Suspend a Tx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_tx_queue_suspend(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_tx_queue *txq = NULL;
|
||
|
|
||
|
txq = (struct pdma_tx_queue *)ctrl->tx_queue[queue];
|
||
|
if (!txq || !(txq->state & PDMA_TX_QUEUE_ACTIVE)) {
|
||
|
return SHR_E_UNAVAIL;
|
||
|
}
|
||
|
|
||
|
if (txq->sem) {
|
||
|
sal_sem_take(txq->sem, SAL_SEM_FOREVER);
|
||
|
}
|
||
|
if (dev->tx_suspend) {
|
||
|
dev->tx_suspend(dev, txq->queue_id);
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Resume a Tx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_tx_queue_resume(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_tx_queue *txq = NULL;
|
||
|
|
||
|
txq = (struct pdma_tx_queue *)ctrl->tx_queue[queue];
|
||
|
if (!txq || !(txq->state & PDMA_TX_QUEUE_ACTIVE)) {
|
||
|
return SHR_E_UNAVAIL;
|
||
|
}
|
||
|
|
||
|
if (txq->sem) {
|
||
|
sal_sem_give(txq->sem);
|
||
|
}
|
||
|
if (dev->tx_resume) {
|
||
|
dev->tx_resume(dev, txq->queue_id);
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Wake up a Tx queue
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_tx_queue_wakeup(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_tx_queue *txq = NULL;
|
||
|
|
||
|
txq = (struct pdma_tx_queue *)ctrl->tx_queue[queue];
|
||
|
if (txq->sem) {
|
||
|
sal_sem_give(txq->sem);
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Transmit a outputing packet
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_tx_queue_xmit(struct pdma_dev *dev, int queue, void *buf)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
struct pdma_tx_queue *txq = NULL;
|
||
|
|
||
|
txq = (struct pdma_tx_queue *)ctrl->tx_queue[queue];
|
||
|
if (!txq || !(txq->state & PDMA_TX_QUEUE_ACTIVE)) {
|
||
|
return SHR_E_UNAVAIL;
|
||
|
}
|
||
|
|
||
|
return hw->dops.pkt_xmit(hw, txq, buf);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Poll a Rx queues
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_rx_queue_poll(struct pdma_dev *dev, int queue, int budget)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_rx_queue *rxq = ctrl->rx_queue[queue];
|
||
|
|
||
|
return bcn_rx_poll(rxq, budget);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Poll a Tx queues
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_tx_queue_poll(struct pdma_dev *dev, int queue, int budget)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_tx_queue *txq = ctrl->tx_queue[queue];
|
||
|
|
||
|
return bcn_tx_poll(txq, budget);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Poll for Rx/Tx queues in a group
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_group_poll(struct pdma_dev *dev, int group, int budget)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = (struct pdma_hw *)ctrl->hw;
|
||
|
struct pdma_rx_queue *rxq = NULL;
|
||
|
struct pdma_tx_queue *txq = NULL;
|
||
|
struct queue_group *grp = &ctrl->grp[group];
|
||
|
int done = 0, done_que, budget_que;
|
||
|
int i;
|
||
|
|
||
|
/* Acknowledge the interrupts */
|
||
|
for (i = 0; i < dev->grp_queues; i++) {
|
||
|
rxq = grp->rx_queue[i];
|
||
|
if (rxq->state & PDMA_RX_QUEUE_ACTIVE) {
|
||
|
if (hw->hdls.chan_intr_query(hw, rxq->chan_id)) {
|
||
|
hw->hdls.chan_clear(hw, rxq->chan_id);
|
||
|
grp->poll_queues |= 1 << i;
|
||
|
} else if (rxq->state & PDMA_RX_QUEUE_BUSY) {
|
||
|
rxq->state &= ~PDMA_RX_QUEUE_BUSY;
|
||
|
grp->poll_queues |= 1 << i;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
txq = grp->tx_queue[i];
|
||
|
if (txq->state & PDMA_TX_QUEUE_ACTIVE) {
|
||
|
if (hw->hdls.chan_intr_query(hw, txq->chan_id)) {
|
||
|
hw->hdls.chan_clear(hw, txq->chan_id);
|
||
|
grp->poll_queues |= 1 << i;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Calculate per queue budget */
|
||
|
if (!grp->poll_queues) {
|
||
|
grp->poll_queues = grp->bm_rxq | grp->bm_txq;
|
||
|
budget_que = budget / grp->nb_rxq;
|
||
|
} else {
|
||
|
budget_que = 0;
|
||
|
for (i = 0; i < dev->grp_queues; i++) {
|
||
|
if (1 << i & grp->bm_rxq & grp->poll_queues) {
|
||
|
budget_que++;
|
||
|
}
|
||
|
}
|
||
|
if (budget_que) {
|
||
|
budget_que = budget / budget_que;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Poll Rx queues */
|
||
|
for (i = 0; i < dev->grp_queues; i++) {
|
||
|
if (1 << i & grp->bm_rxq & grp->poll_queues) {
|
||
|
rxq = grp->rx_queue[i];
|
||
|
done_que = bcn_rx_poll(rxq, budget_que);
|
||
|
if (done_que < budget_que) {
|
||
|
grp->poll_queues &= ~(1 << i);
|
||
|
}
|
||
|
done += done_que;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Poll Tx queues */
|
||
|
for (i = 0; i < dev->grp_queues; i++) {
|
||
|
txq = grp->tx_queue[i];
|
||
|
if (1 << i & grp->bm_txq & grp->poll_queues && !txq->free_thresh) {
|
||
|
if (bcn_tx_poll(txq, budget) < budget) {
|
||
|
grp->poll_queues &= ~(1 << i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return grp->poll_queues ? budget : done;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Dump a Rx ring
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_rx_ring_dump(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = ctrl->hw;
|
||
|
struct pdma_rx_queue *rxq = NULL;
|
||
|
|
||
|
if ((uint32_t)queue >= ctrl->nb_rxq) {
|
||
|
return SHR_E_PARAM;
|
||
|
}
|
||
|
|
||
|
rxq = (struct pdma_rx_queue *)ctrl->rx_queue[queue];
|
||
|
if (rxq->state & PDMA_RX_QUEUE_ACTIVE) {
|
||
|
hw->dops.rx_ring_dump(hw, rxq);
|
||
|
}
|
||
|
if (dev->mode == DEV_MODE_HNET) {
|
||
|
rxq = (struct pdma_rx_queue *)ctrl->vnet_rxq[queue];
|
||
|
hw->dops.rx_ring_dump(hw, rxq);
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Dump a Tx ring
|
||
|
*/
|
||
|
int
|
||
|
bcmcnet_pdma_tx_ring_dump(struct pdma_dev *dev, int queue)
|
||
|
{
|
||
|
struct dev_ctrl *ctrl = &dev->ctrl;
|
||
|
struct pdma_hw *hw = ctrl->hw;
|
||
|
struct pdma_tx_queue *txq = NULL;
|
||
|
|
||
|
if ((uint32_t)queue >= ctrl->nb_txq) {
|
||
|
return SHR_E_PARAM;
|
||
|
}
|
||
|
|
||
|
txq = (struct pdma_tx_queue *)ctrl->tx_queue[queue];
|
||
|
if (txq->state & PDMA_TX_QUEUE_ACTIVE) {
|
||
|
hw->dops.tx_ring_dump(hw, txq);
|
||
|
}
|
||
|
if (dev->mode == DEV_MODE_HNET) {
|
||
|
txq = (struct pdma_tx_queue *)ctrl->vnet_txq[queue];
|
||
|
hw->dops.tx_ring_dump(hw, txq);
|
||
|
}
|
||
|
|
||
|
return SHR_E_NONE;
|
||
|
}
|
||
|
|