mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-19 16:38:53 +09:00
![Suneel Garapati](/assets/img/avatar_default.png)
Adds support for Network Interface controllers found on OcteonTX2 SoC platforms. Signed-off-by: Suneel Garapati <sgarapati@marvell.com> Signed-off-by: Stefan Roese <sr@denx.de> Cc: Joe Hershberger <joe.hershberger@ni.com>
832 lines
22 KiB
C
832 lines
22 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2018 Marvell International Ltd.
|
|
*/
|
|
|
|
#include <dm.h>
|
|
#include <errno.h>
|
|
#include <log.h>
|
|
#include <malloc.h>
|
|
#include <memalign.h>
|
|
#include <misc.h>
|
|
#include <net.h>
|
|
#include <pci.h>
|
|
#include <watchdog.h>
|
|
|
|
#include <asm/arch/board.h>
|
|
#include <asm/arch/csrs/csrs-lmt.h>
|
|
#include <asm/io.h>
|
|
#include <asm/types.h>
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/log2.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "nix.h"
|
|
#include "lmt.h"
|
|
#include "cgx.h"
|
|
|
|
/**
|
|
* NIX needs a lot of memory areas. Rather than handle all the failure cases,
|
|
* we'll use a wrapper around alloc that prints an error if a memory
|
|
* allocation fails.
|
|
*
|
|
* @param num_elements
|
|
* Number of elements to allocate
|
|
* @param elem_size Size of each element
|
|
* @param msg Text string to show when allocation fails
|
|
*
|
|
* @return A valid memory location or NULL on failure
|
|
*/
|
|
static void *nix_memalloc(int num_elements, size_t elem_size, const char *msg)
|
|
{
|
|
size_t alloc_size = num_elements * elem_size;
|
|
void *base = memalign(CONFIG_SYS_CACHELINE_SIZE, alloc_size);
|
|
|
|
if (!base)
|
|
printf("NIX: Mem alloc failed for %s (%d * %zu = %zu bytes)\n",
|
|
msg ? msg : __func__, num_elements, elem_size,
|
|
alloc_size);
|
|
else
|
|
memset(base, 0, alloc_size);
|
|
|
|
debug("NIX: Memory alloc for %s (%d * %zu = %zu bytes) at %p\n",
|
|
msg ? msg : __func__, num_elements, elem_size, alloc_size, base);
|
|
return base;
|
|
}
|
|
|
|
int npc_lf_setup(struct nix *nix)
|
|
{
|
|
int err;
|
|
|
|
err = npc_lf_admin_setup(nix);
|
|
if (err) {
|
|
printf("%s: Error setting up npc lf admin\n", __func__);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int npa_setup_pool(struct npa *npa, u32 pool_id,
|
|
size_t buffer_size, u32 queue_length, void *buffers[])
|
|
{
|
|
struct {
|
|
union npa_lf_aura_op_free0 f0;
|
|
union npa_lf_aura_op_free1 f1;
|
|
} aura_descr;
|
|
int index;
|
|
|
|
for (index = 0; index < queue_length; index++) {
|
|
buffers[index] = memalign(CONFIG_SYS_CACHELINE_SIZE,
|
|
buffer_size);
|
|
if (!buffers[index]) {
|
|
printf("%s: Out of memory %d, size: %zu\n",
|
|
__func__, index, buffer_size);
|
|
return -ENOMEM;
|
|
}
|
|
debug("%s: allocating buffer %d, addr %p size: %zu\n",
|
|
__func__, index, buffers[index], buffer_size);
|
|
|
|
/* Add the newly obtained pointer to the pool. 128 bit
|
|
* writes only.
|
|
*/
|
|
aura_descr.f0.s.addr = (u64)buffers[index];
|
|
aura_descr.f1.u = 0;
|
|
aura_descr.f1.s.aura = pool_id;
|
|
st128(npa->npa_base + NPA_LF_AURA_OP_FREE0(),
|
|
aura_descr.f0.u, aura_descr.f1.u);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int npa_lf_setup(struct nix *nix)
|
|
{
|
|
struct rvu_pf *rvu = dev_get_priv(nix->dev);
|
|
struct nix_af *nix_af = nix->nix_af;
|
|
struct npa *npa;
|
|
union npa_af_const npa_af_const;
|
|
union npa_aura_s *aura;
|
|
union npa_pool_s *pool;
|
|
union rvu_func_addr_s block_addr;
|
|
int idx;
|
|
int stack_page_pointers;
|
|
int stack_page_bytes;
|
|
int err;
|
|
|
|
npa = (struct npa *)calloc(1, sizeof(struct npa));
|
|
if (!npa) {
|
|
printf("%s: out of memory for npa instance\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
block_addr.u = 0;
|
|
block_addr.s.block = RVU_BLOCK_ADDR_E_NPA;
|
|
npa->npa_base = rvu->pf_base + block_addr.u;
|
|
npa->npa_af = nix_af->npa_af;
|
|
nix->npa = npa;
|
|
|
|
npa_af_const.u = npa_af_reg_read(npa->npa_af, NPA_AF_CONST());
|
|
stack_page_pointers = npa_af_const.s.stack_page_ptrs;
|
|
stack_page_bytes = npa_af_const.s.stack_page_bytes;
|
|
|
|
npa->stack_pages[NPA_POOL_RX] = (RQ_QLEN + stack_page_pointers - 1) /
|
|
stack_page_pointers;
|
|
npa->stack_pages[NPA_POOL_TX] = (SQ_QLEN + stack_page_pointers - 1) /
|
|
stack_page_pointers;
|
|
npa->stack_pages[NPA_POOL_SQB] = (SQB_QLEN + stack_page_pointers - 1) /
|
|
stack_page_pointers;
|
|
npa->pool_stack_pointers = stack_page_pointers;
|
|
|
|
npa->q_len[NPA_POOL_RX] = RQ_QLEN;
|
|
npa->q_len[NPA_POOL_TX] = SQ_QLEN;
|
|
npa->q_len[NPA_POOL_SQB] = SQB_QLEN;
|
|
|
|
npa->buf_size[NPA_POOL_RX] = MAX_MTU + CONFIG_SYS_CACHELINE_SIZE;
|
|
npa->buf_size[NPA_POOL_TX] = MAX_MTU + CONFIG_SYS_CACHELINE_SIZE;
|
|
npa->buf_size[NPA_POOL_SQB] = nix_af->sqb_size;
|
|
|
|
npa->aura_ctx = nix_memalloc(NPA_POOL_COUNT,
|
|
sizeof(union npa_aura_s),
|
|
"aura context");
|
|
if (!npa->aura_ctx) {
|
|
printf("%s: Out of memory for aura context\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (idx = 0; idx < NPA_POOL_COUNT; idx++) {
|
|
npa->pool_ctx[idx] = nix_memalloc(1,
|
|
sizeof(union npa_pool_s),
|
|
"pool context");
|
|
if (!npa->pool_ctx[idx]) {
|
|
printf("%s: Out of memory for pool context\n",
|
|
__func__);
|
|
return -ENOMEM;
|
|
}
|
|
npa->pool_stack[idx] = nix_memalloc(npa->stack_pages[idx],
|
|
stack_page_bytes,
|
|
"pool stack");
|
|
if (!npa->pool_stack[idx]) {
|
|
printf("%s: Out of memory for pool stack\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
err = npa_lf_admin_setup(npa, nix->lf, (dma_addr_t)npa->aura_ctx);
|
|
if (err) {
|
|
printf("%s: Error setting up NPA LF admin for lf %d\n",
|
|
__func__, nix->lf);
|
|
return err;
|
|
}
|
|
|
|
/* Set up the auras */
|
|
for (idx = 0; idx < NPA_POOL_COUNT; idx++) {
|
|
aura = npa->aura_ctx + (idx * sizeof(union npa_aura_s));
|
|
pool = npa->pool_ctx[idx];
|
|
debug("%s aura %p pool %p\n", __func__, aura, pool);
|
|
memset(aura, 0, sizeof(union npa_aura_s));
|
|
aura->s.fc_ena = 0;
|
|
aura->s.pool_addr = (u64)npa->pool_ctx[idx];
|
|
debug("%s aura.s.pool_addr %llx pool_addr %p\n", __func__,
|
|
aura->s.pool_addr, npa->pool_ctx[idx]);
|
|
aura->s.shift = 64 - __builtin_clzll(npa->q_len[idx]) - 8;
|
|
aura->s.count = npa->q_len[idx];
|
|
aura->s.limit = npa->q_len[idx];
|
|
aura->s.ena = 1;
|
|
err = npa_attach_aura(nix_af, nix->lf, aura, idx);
|
|
if (err)
|
|
return err;
|
|
|
|
memset(pool, 0, sizeof(*pool));
|
|
pool->s.fc_ena = 0;
|
|
pool->s.nat_align = 1;
|
|
pool->s.stack_base = (u64)(npa->pool_stack[idx]);
|
|
debug("%s pool.s.stack_base %llx stack_base %p\n", __func__,
|
|
pool->s.stack_base, npa->pool_stack[idx]);
|
|
pool->s.buf_size =
|
|
npa->buf_size[idx] / CONFIG_SYS_CACHELINE_SIZE;
|
|
pool->s.stack_max_pages = npa->stack_pages[idx];
|
|
pool->s.shift =
|
|
64 - __builtin_clzll(npa->pool_stack_pointers) - 8;
|
|
pool->s.ptr_start = 0;
|
|
pool->s.ptr_end = (1ULL << 40) - 1;
|
|
pool->s.ena = 1;
|
|
err = npa_attach_pool(nix_af, nix->lf, pool, idx);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
for (idx = 0; idx < NPA_POOL_COUNT; idx++) {
|
|
npa->buffers[idx] = nix_memalloc(npa->q_len[idx],
|
|
sizeof(void *),
|
|
"buffers");
|
|
if (!npa->buffers[idx]) {
|
|
printf("%s: Out of memory\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
for (idx = 0; idx < NPA_POOL_COUNT; idx++) {
|
|
err = npa_setup_pool(npa, idx, npa->buf_size[idx],
|
|
npa->q_len[idx], npa->buffers[idx]);
|
|
if (err) {
|
|
printf("%s: Error setting up pool %d\n",
|
|
__func__, idx);
|
|
return err;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int npa_lf_shutdown(struct nix *nix)
|
|
{
|
|
struct npa *npa = nix->npa;
|
|
int err;
|
|
int pool;
|
|
|
|
err = npa_lf_admin_shutdown(nix->nix_af, nix->lf, NPA_POOL_COUNT);
|
|
if (err) {
|
|
printf("%s: Error %d shutting down NPA LF admin\n",
|
|
__func__, err);
|
|
return err;
|
|
}
|
|
free(npa->aura_ctx);
|
|
npa->aura_ctx = NULL;
|
|
|
|
for (pool = 0; pool < NPA_POOL_COUNT; pool++) {
|
|
free(npa->pool_ctx[pool]);
|
|
npa->pool_ctx[pool] = NULL;
|
|
free(npa->pool_stack[pool]);
|
|
npa->pool_stack[pool] = NULL;
|
|
free(npa->buffers[pool]);
|
|
npa->buffers[pool] = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int nix_lf_setup(struct nix *nix)
|
|
{
|
|
struct nix_af *nix_af = nix->nix_af;
|
|
int idx;
|
|
int err = -1;
|
|
|
|
/* Alloc NIX RQ HW context memory */
|
|
nix->rq_ctx_base = nix_memalloc(nix->rq_cnt, nix_af->rq_ctx_sz,
|
|
"RQ CTX");
|
|
if (!nix->rq_ctx_base)
|
|
goto error;
|
|
memset(nix->rq_ctx_base, 0, nix_af->rq_ctx_sz);
|
|
|
|
/* Alloc NIX SQ HW context memory */
|
|
nix->sq_ctx_base = nix_memalloc(nix->sq_cnt, nix_af->sq_ctx_sz,
|
|
"SQ CTX");
|
|
if (!nix->sq_ctx_base)
|
|
goto error;
|
|
memset(nix->sq_ctx_base, 0, nix_af->sq_ctx_sz);
|
|
|
|
/* Alloc NIX CQ HW context memory */
|
|
nix->cq_ctx_base = nix_memalloc(nix->cq_cnt, nix_af->cq_ctx_sz,
|
|
"CQ CTX");
|
|
if (!nix->cq_ctx_base)
|
|
goto error;
|
|
memset(nix->cq_ctx_base, 0, nix_af->cq_ctx_sz * NIX_CQ_COUNT);
|
|
/* Alloc NIX CQ Ring memory */
|
|
for (idx = 0; idx < NIX_CQ_COUNT; idx++) {
|
|
err = qmem_alloc(&nix->cq[idx], CQ_ENTRIES, CQ_ENTRY_SIZE);
|
|
if (err)
|
|
goto error;
|
|
}
|
|
|
|
/* Alloc memory for Qints HW contexts */
|
|
nix->qint_base = nix_memalloc(nix_af->qints, nix_af->qint_ctx_sz,
|
|
"Qint CTX");
|
|
if (!nix->qint_base)
|
|
goto error;
|
|
/* Alloc memory for CQints HW contexts */
|
|
nix->cint_base = nix_memalloc(nix_af->cints, nix_af->cint_ctx_sz,
|
|
"Cint CTX");
|
|
if (!nix->cint_base)
|
|
goto error;
|
|
/* Alloc NIX RSS HW context memory and config the base */
|
|
nix->rss_base = nix_memalloc(nix->rss_grps, nix_af->rsse_ctx_sz,
|
|
"RSS CTX");
|
|
if (!nix->rss_base)
|
|
goto error;
|
|
|
|
err = nix_lf_admin_setup(nix);
|
|
if (err) {
|
|
printf("%s: Error setting up LF\n", __func__);
|
|
goto error;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
if (nix->rq_ctx_base)
|
|
free(nix->rq_ctx_base);
|
|
nix->rq_ctx_base = NULL;
|
|
if (nix->rq_ctx_base)
|
|
free(nix->rq_ctx_base);
|
|
nix->rq_ctx_base = NULL;
|
|
if (nix->sq_ctx_base)
|
|
free(nix->sq_ctx_base);
|
|
nix->sq_ctx_base = NULL;
|
|
if (nix->cq_ctx_base)
|
|
free(nix->cq_ctx_base);
|
|
nix->cq_ctx_base = NULL;
|
|
|
|
for (idx = 0; idx < NIX_CQ_COUNT; idx++)
|
|
qmem_free(&nix->cq[idx]);
|
|
|
|
return err;
|
|
}
|
|
|
|
int nix_lf_shutdown(struct nix *nix)
|
|
{
|
|
struct nix_af *nix_af = nix->nix_af;
|
|
int index;
|
|
int err;
|
|
|
|
err = nix_lf_admin_shutdown(nix_af, nix->lf, nix->cq_cnt,
|
|
nix->rq_cnt, nix->sq_cnt);
|
|
if (err) {
|
|
printf("%s: Error shutting down LF admin\n", __func__);
|
|
return err;
|
|
}
|
|
|
|
if (nix->rq_ctx_base)
|
|
free(nix->rq_ctx_base);
|
|
nix->rq_ctx_base = NULL;
|
|
if (nix->rq_ctx_base)
|
|
free(nix->rq_ctx_base);
|
|
nix->rq_ctx_base = NULL;
|
|
if (nix->sq_ctx_base)
|
|
free(nix->sq_ctx_base);
|
|
nix->sq_ctx_base = NULL;
|
|
if (nix->cq_ctx_base)
|
|
free(nix->cq_ctx_base);
|
|
nix->cq_ctx_base = NULL;
|
|
|
|
for (index = 0; index < NIX_CQ_COUNT; index++)
|
|
qmem_free(&nix->cq[index]);
|
|
|
|
debug("%s: nix lf %d reset --\n", __func__, nix->lf);
|
|
return 0;
|
|
}
|
|
|
|
struct nix *nix_lf_alloc(struct udevice *dev)
|
|
{
|
|
union rvu_func_addr_s block_addr;
|
|
struct nix *nix;
|
|
struct rvu_pf *rvu = dev_get_priv(dev);
|
|
struct rvu_af *rvu_af = dev_get_priv(rvu->afdev);
|
|
union rvu_pf_func_s pf_func;
|
|
int err;
|
|
|
|
debug("%s(%s )\n", __func__, dev->name);
|
|
|
|
nix = (struct nix *)calloc(1, sizeof(*nix));
|
|
if (!nix) {
|
|
printf("%s: Out of memory for nix instance\n", __func__);
|
|
return NULL;
|
|
}
|
|
nix->nix_af = rvu_af->nix_af;
|
|
|
|
block_addr.u = 0;
|
|
block_addr.s.block = RVU_BLOCK_ADDR_E_NIXX(0);
|
|
nix->nix_base = rvu->pf_base + block_addr.u;
|
|
block_addr.u = 0;
|
|
block_addr.s.block = RVU_BLOCK_ADDR_E_NPC;
|
|
nix->npc_base = rvu->pf_base + block_addr.u;
|
|
block_addr.u = 0;
|
|
block_addr.s.block = RVU_BLOCK_ADDR_E_LMT;
|
|
nix->lmt_base = rvu->pf_base + block_addr.u;
|
|
|
|
pf_func.u = 0;
|
|
pf_func.s.pf = rvu->pfid;
|
|
nix->pf_func = pf_func.u;
|
|
nix->lf = rvu->nix_lfid;
|
|
nix->pf = rvu->pfid;
|
|
nix->dev = dev;
|
|
nix->sq_cnt = 1;
|
|
nix->rq_cnt = 1;
|
|
nix->rss_grps = 1;
|
|
nix->cq_cnt = 2;
|
|
nix->xqe_sz = NIX_CQE_SIZE_W16;
|
|
|
|
nix->lmac = nix_get_cgx_lmac(nix->pf);
|
|
if (!nix->lmac) {
|
|
printf("%s: Error: could not find lmac for pf %d\n",
|
|
__func__, nix->pf);
|
|
free(nix);
|
|
return NULL;
|
|
}
|
|
nix->lmac->link_num =
|
|
NIX_LINK_E_CGXX_LMACX(nix->lmac->cgx->cgx_id,
|
|
nix->lmac->lmac_id);
|
|
nix->lmac->chan_num =
|
|
NIX_CHAN_E_CGXX_LMACX_CHX(nix->lmac->cgx->cgx_id,
|
|
nix->lmac->lmac_id, 0);
|
|
/* This is rx pkind in 1:1 mapping to NIX_LINK_E */
|
|
nix->lmac->pknd = nix->lmac->link_num;
|
|
|
|
cgx_lmac_set_pkind(nix->lmac, nix->lmac->lmac_id, nix->lmac->pknd);
|
|
debug("%s(%s CGX%x LMAC%x)\n", __func__, dev->name,
|
|
nix->lmac->cgx->cgx_id, nix->lmac->lmac_id);
|
|
debug("%s(%s Link %x Chan %x Pknd %x)\n", __func__, dev->name,
|
|
nix->lmac->link_num, nix->lmac->chan_num, nix->lmac->pknd);
|
|
|
|
err = npa_lf_setup(nix);
|
|
if (err)
|
|
return NULL;
|
|
|
|
err = npc_lf_setup(nix);
|
|
if (err)
|
|
return NULL;
|
|
|
|
err = nix_lf_setup(nix);
|
|
if (err)
|
|
return NULL;
|
|
|
|
return nix;
|
|
}
|
|
|
|
u64 npa_aura_op_alloc(struct npa *npa, u64 aura_id)
|
|
{
|
|
union npa_lf_aura_op_allocx op_allocx;
|
|
|
|
op_allocx.u = atomic_fetch_and_add64_nosync(npa->npa_base +
|
|
NPA_LF_AURA_OP_ALLOCX(0), aura_id);
|
|
return op_allocx.s.addr;
|
|
}
|
|
|
|
u64 nix_cq_op_status(struct nix *nix, u64 cq_id)
|
|
{
|
|
union nixx_lf_cq_op_status op_status;
|
|
s64 *reg = nix->nix_base + NIXX_LF_CQ_OP_STATUS();
|
|
|
|
op_status.u = atomic_fetch_and_add64_nosync(reg, cq_id << 32);
|
|
return op_status.u;
|
|
}
|
|
|
|
/* TX */
|
|
static inline void nix_write_lmt(struct nix *nix, void *buffer,
|
|
int num_words)
|
|
{
|
|
int i;
|
|
|
|
u64 *lmt_ptr = lmt_store_ptr(nix);
|
|
u64 *ptr = buffer;
|
|
|
|
debug("%s lmt_ptr %p %p\n", __func__, nix->lmt_base, lmt_ptr);
|
|
for (i = 0; i < num_words; i++) {
|
|
debug("%s data %llx lmt_ptr %p\n", __func__, ptr[i],
|
|
lmt_ptr + i);
|
|
lmt_ptr[i] = ptr[i];
|
|
}
|
|
}
|
|
|
|
void nix_cqe_tx_pkt_handler(struct nix *nix, void *cqe)
|
|
{
|
|
union nix_cqe_hdr_s *txcqe = (union nix_cqe_hdr_s *)cqe;
|
|
|
|
debug("%s: txcqe: %p\n", __func__, txcqe);
|
|
|
|
if (txcqe->s.cqe_type != NIX_XQE_TYPE_E_SEND) {
|
|
printf("%s: Error: Unsupported CQ header type %d\n",
|
|
__func__, txcqe->s.cqe_type);
|
|
return;
|
|
}
|
|
nix_pf_reg_write(nix, NIXX_LF_CQ_OP_DOOR(),
|
|
(NIX_CQ_TX << 32) | 1);
|
|
}
|
|
|
|
void nix_lf_flush_tx(struct udevice *dev)
|
|
{
|
|
struct rvu_pf *rvu = dev_get_priv(dev);
|
|
struct nix *nix = rvu->nix;
|
|
union nixx_lf_cq_op_status op_status;
|
|
u32 head, tail;
|
|
void *cq_tx_base = nix->cq[NIX_CQ_TX].base;
|
|
union nix_cqe_hdr_s *cqe;
|
|
|
|
/* ack tx cqe entries */
|
|
op_status.u = nix_cq_op_status(nix, NIX_CQ_TX);
|
|
head = op_status.s.head;
|
|
tail = op_status.s.tail;
|
|
head &= (nix->cq[NIX_CQ_TX].qsize - 1);
|
|
tail &= (nix->cq[NIX_CQ_TX].qsize - 1);
|
|
|
|
debug("%s cq tx head %d tail %d\n", __func__, head, tail);
|
|
while (head != tail) {
|
|
cqe = cq_tx_base + head * nix->cq[NIX_CQ_TX].entry_sz;
|
|
nix_cqe_tx_pkt_handler(nix, cqe);
|
|
op_status.u = nix_cq_op_status(nix, NIX_CQ_TX);
|
|
head = op_status.s.head;
|
|
tail = op_status.s.tail;
|
|
head &= (nix->cq[NIX_CQ_TX].qsize - 1);
|
|
tail &= (nix->cq[NIX_CQ_TX].qsize - 1);
|
|
debug("%s cq tx head %d tail %d\n", __func__, head, tail);
|
|
}
|
|
}
|
|
|
|
int nix_lf_xmit(struct udevice *dev, void *pkt, int pkt_len)
|
|
{
|
|
struct rvu_pf *rvu = dev_get_priv(dev);
|
|
struct nix *nix = rvu->nix;
|
|
struct nix_tx_dr tx_dr;
|
|
int dr_sz = (sizeof(struct nix_tx_dr) + 15) / 16 - 1;
|
|
s64 result;
|
|
void *packet;
|
|
|
|
nix_lf_flush_tx(dev);
|
|
memset((void *)&tx_dr, 0, sizeof(struct nix_tx_dr));
|
|
/* Dump TX packet in to NPA buffer */
|
|
packet = (void *)npa_aura_op_alloc(nix->npa, NPA_POOL_TX);
|
|
if (!packet) {
|
|
printf("%s TX buffers unavailable\n", __func__);
|
|
return -1;
|
|
}
|
|
memcpy(packet, pkt, pkt_len);
|
|
debug("%s TX buffer %p\n", __func__, packet);
|
|
|
|
tx_dr.hdr.s.aura = NPA_POOL_TX;
|
|
tx_dr.hdr.s.df = 0;
|
|
tx_dr.hdr.s.pnc = 1;
|
|
tx_dr.hdr.s.sq = 0;
|
|
tx_dr.hdr.s.total = pkt_len;
|
|
tx_dr.hdr.s.sizem1 = dr_sz - 2; /* FIXME - for now hdr+sg+sg1addr */
|
|
debug("%s dr_sz %d\n", __func__, dr_sz);
|
|
|
|
tx_dr.tx_sg.s.segs = 1;
|
|
tx_dr.tx_sg.s.subdc = NIX_SUBDC_E_SG;
|
|
tx_dr.tx_sg.s.seg1_size = pkt_len;
|
|
tx_dr.tx_sg.s.ld_type = NIX_SENDLDTYPE_E_LDT;
|
|
tx_dr.sg1_addr = (dma_addr_t)packet;
|
|
|
|
#define DEBUG_PKT
|
|
#ifdef DEBUG_PKT
|
|
debug("TX PKT Data\n");
|
|
for (int i = 0; i < pkt_len; i++) {
|
|
if (i && (i % 8 == 0))
|
|
debug("\n");
|
|
debug("%02x ", *((u8 *)pkt + i));
|
|
}
|
|
debug("\n");
|
|
#endif
|
|
do {
|
|
nix_write_lmt(nix, &tx_dr, (dr_sz - 1) * 2);
|
|
__iowmb();
|
|
result = lmt_submit((u64)(nix->nix_base +
|
|
NIXX_LF_OP_SENDX(0)));
|
|
WATCHDOG_RESET();
|
|
} while (result == 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* RX */
|
|
void nix_lf_flush_rx(struct udevice *dev)
|
|
{
|
|
struct rvu_pf *rvu = dev_get_priv(dev);
|
|
struct nix *nix = rvu->nix;
|
|
union nixx_lf_cq_op_status op_status;
|
|
void *cq_rx_base = nix->cq[NIX_CQ_RX].base;
|
|
struct nix_rx_dr *rx_dr;
|
|
union nix_rx_parse_s *rxparse;
|
|
u32 head, tail;
|
|
u32 rx_cqe_sz = nix->cq[NIX_CQ_RX].entry_sz;
|
|
u64 *seg;
|
|
|
|
/* flush rx cqe entries */
|
|
op_status.u = nix_cq_op_status(nix, NIX_CQ_RX);
|
|
head = op_status.s.head;
|
|
tail = op_status.s.tail;
|
|
head &= (nix->cq[NIX_CQ_RX].qsize - 1);
|
|
tail &= (nix->cq[NIX_CQ_RX].qsize - 1);
|
|
|
|
debug("%s cq rx head %d tail %d\n", __func__, head, tail);
|
|
while (head != tail) {
|
|
rx_dr = (struct nix_rx_dr *)cq_rx_base + head * rx_cqe_sz;
|
|
rxparse = &rx_dr->rx_parse;
|
|
|
|
debug("%s: rx parse: %p\n", __func__, rxparse);
|
|
debug("%s: rx parse: desc_sizem1 %x pkt_lenm1 %x\n",
|
|
__func__, rxparse->s.desc_sizem1, rxparse->s.pkt_lenm1);
|
|
|
|
seg = (dma_addr_t *)(&rx_dr->rx_sg + 1);
|
|
|
|
st128(nix->npa->npa_base + NPA_LF_AURA_OP_FREE0(),
|
|
seg[0], (1ULL << 63) | NPA_POOL_RX);
|
|
|
|
debug("%s return %llx to NPA\n", __func__, seg[0]);
|
|
nix_pf_reg_write(nix, NIXX_LF_CQ_OP_DOOR(),
|
|
(NIX_CQ_RX << 32) | 1);
|
|
|
|
op_status.u = nix_cq_op_status(nix, NIX_CQ_RX);
|
|
head = op_status.s.head;
|
|
tail = op_status.s.tail;
|
|
head &= (nix->cq[NIX_CQ_RX].qsize - 1);
|
|
tail &= (nix->cq[NIX_CQ_RX].qsize - 1);
|
|
debug("%s cq rx head %d tail %d\n", __func__, head, tail);
|
|
}
|
|
}
|
|
|
|
int nix_lf_free_pkt(struct udevice *dev, uchar *pkt, int pkt_len)
|
|
{
|
|
struct rvu_pf *rvu = dev_get_priv(dev);
|
|
struct nix *nix = rvu->nix;
|
|
|
|
/* Return rx packet to NPA */
|
|
debug("%s return %p to NPA\n", __func__, pkt);
|
|
st128(nix->npa->npa_base + NPA_LF_AURA_OP_FREE0(), (u64)pkt,
|
|
(1ULL << 63) | NPA_POOL_RX);
|
|
nix_pf_reg_write(nix, NIXX_LF_CQ_OP_DOOR(),
|
|
(NIX_CQ_RX << 32) | 1);
|
|
|
|
nix_lf_flush_tx(dev);
|
|
return 0;
|
|
}
|
|
|
|
int nix_lf_recv(struct udevice *dev, int flags, uchar **packetp)
|
|
{
|
|
struct rvu_pf *rvu = dev_get_priv(dev);
|
|
struct nix *nix = rvu->nix;
|
|
union nixx_lf_cq_op_status op_status;
|
|
void *cq_rx_base = nix->cq[NIX_CQ_RX].base;
|
|
struct nix_rx_dr *rx_dr;
|
|
union nix_rx_parse_s *rxparse;
|
|
void *pkt, *cqe;
|
|
int pkt_len = 0;
|
|
u64 *addr;
|
|
u32 head, tail;
|
|
|
|
/* fetch rx cqe entries */
|
|
op_status.u = nix_cq_op_status(nix, NIX_CQ_RX);
|
|
head = op_status.s.head;
|
|
tail = op_status.s.tail;
|
|
head &= (nix->cq[NIX_CQ_RX].qsize - 1);
|
|
tail &= (nix->cq[NIX_CQ_RX].qsize - 1);
|
|
debug("%s cq rx head %d tail %d\n", __func__, head, tail);
|
|
if (head == tail)
|
|
return -EAGAIN;
|
|
|
|
debug("%s: rx_base %p head %d sz %d\n", __func__, cq_rx_base, head,
|
|
nix->cq[NIX_CQ_RX].entry_sz);
|
|
cqe = cq_rx_base + head * nix->cq[NIX_CQ_RX].entry_sz;
|
|
rx_dr = (struct nix_rx_dr *)cqe;
|
|
rxparse = &rx_dr->rx_parse;
|
|
|
|
debug("%s: rx completion: %p\n", __func__, cqe);
|
|
debug("%s: rx dr: %p\n", __func__, rx_dr);
|
|
debug("%s: rx parse: %p\n", __func__, rxparse);
|
|
debug("%s: rx parse: desc_sizem1 %x pkt_lenm1 %x\n",
|
|
__func__, rxparse->s.desc_sizem1, rxparse->s.pkt_lenm1);
|
|
debug("%s: rx parse: pkind %x chan %x\n",
|
|
__func__, rxparse->s.pkind, rxparse->s.chan);
|
|
|
|
if (rx_dr->hdr.s.cqe_type != NIX_XQE_TYPE_E_RX) {
|
|
printf("%s: Error: Unsupported CQ header type in Rx %d\n",
|
|
__func__, rx_dr->hdr.s.cqe_type);
|
|
return -1;
|
|
}
|
|
|
|
pkt_len = rxparse->s.pkt_lenm1 + 1;
|
|
addr = (dma_addr_t *)(&rx_dr->rx_sg + 1);
|
|
pkt = (void *)addr[0];
|
|
|
|
debug("%s: segs: %d (%d@0x%llx, %d@0x%llx, %d@0x%llx)\n", __func__,
|
|
rx_dr->rx_sg.s.segs, rx_dr->rx_sg.s.seg1_size, addr[0],
|
|
rx_dr->rx_sg.s.seg2_size, addr[1],
|
|
rx_dr->rx_sg.s.seg3_size, addr[2]);
|
|
if (pkt_len < rx_dr->rx_sg.s.seg1_size + rx_dr->rx_sg.s.seg2_size +
|
|
rx_dr->rx_sg.s.seg3_size) {
|
|
debug("%s: Error: rx buffer size too small\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
__iowmb();
|
|
#define DEBUG_PKT
|
|
#ifdef DEBUG_PKT
|
|
debug("RX PKT Data\n");
|
|
for (int i = 0; i < pkt_len; i++) {
|
|
if (i && (i % 8 == 0))
|
|
debug("\n");
|
|
debug("%02x ", *((u8 *)pkt + i));
|
|
}
|
|
debug("\n");
|
|
#endif
|
|
|
|
*packetp = (uchar *)pkt;
|
|
|
|
return pkt_len;
|
|
}
|
|
|
|
int nix_lf_setup_mac(struct udevice *dev)
|
|
{
|
|
struct rvu_pf *rvu = dev_get_priv(dev);
|
|
struct nix *nix = rvu->nix;
|
|
struct eth_pdata *pdata = dev_get_platdata(dev);
|
|
|
|
/* If lower level firmware fails to set proper MAC
|
|
* u-boot framework updates MAC to random address.
|
|
* Use this hook to update mac address in cgx lmac
|
|
* and call mac filter setup to update new address.
|
|
*/
|
|
if (memcmp(nix->lmac->mac_addr, pdata->enetaddr, ARP_HLEN)) {
|
|
memcpy(nix->lmac->mac_addr, pdata->enetaddr, 6);
|
|
eth_env_set_enetaddr_by_index("eth", rvu->dev->seq,
|
|
pdata->enetaddr);
|
|
cgx_lmac_mac_filter_setup(nix->lmac);
|
|
/* Update user given MAC address to ATF for update
|
|
* in sh_fwdata to use in Linux.
|
|
*/
|
|
cgx_intf_set_macaddr(dev);
|
|
debug("%s: lMAC %pM\n", __func__, nix->lmac->mac_addr);
|
|
debug("%s: pMAC %pM\n", __func__, pdata->enetaddr);
|
|
}
|
|
debug("%s: setupMAC %pM\n", __func__, pdata->enetaddr);
|
|
return 0;
|
|
}
|
|
|
|
void nix_lf_halt(struct udevice *dev)
|
|
{
|
|
struct rvu_pf *rvu = dev_get_priv(dev);
|
|
struct nix *nix = rvu->nix;
|
|
|
|
cgx_lmac_rx_tx_enable(nix->lmac, nix->lmac->lmac_id, false);
|
|
|
|
mdelay(1);
|
|
|
|
/* Flush tx and rx descriptors */
|
|
nix_lf_flush_rx(dev);
|
|
nix_lf_flush_tx(dev);
|
|
}
|
|
|
|
int nix_lf_init(struct udevice *dev)
|
|
{
|
|
struct rvu_pf *rvu = dev_get_priv(dev);
|
|
struct nix *nix = rvu->nix;
|
|
struct lmac *lmac = nix->lmac;
|
|
int ret;
|
|
u64 link_sts;
|
|
u8 link, speed;
|
|
u16 errcode;
|
|
|
|
printf("Waiting for CGX%d LMAC%d [%s] link status...",
|
|
lmac->cgx->cgx_id, lmac->lmac_id,
|
|
lmac_type_to_str[lmac->lmac_type]);
|
|
|
|
if (lmac->init_pend) {
|
|
/* Bring up LMAC */
|
|
ret = cgx_lmac_link_enable(lmac, lmac->lmac_id,
|
|
true, &link_sts);
|
|
lmac->init_pend = 0;
|
|
} else {
|
|
ret = cgx_lmac_link_status(lmac, lmac->lmac_id, &link_sts);
|
|
}
|
|
|
|
if (ret) {
|
|
printf(" [Down]\n");
|
|
return -1;
|
|
}
|
|
|
|
link = link_sts & 0x1;
|
|
speed = (link_sts >> 2) & 0xf;
|
|
errcode = (link_sts >> 6) & 0x2ff;
|
|
debug("%s: link %x speed %x errcode %x\n",
|
|
__func__, link, speed, errcode);
|
|
|
|
/* Print link status */
|
|
printf(" [%s]\n", link ? lmac_speed_to_str[speed] : "Down");
|
|
if (!link)
|
|
return -1;
|
|
|
|
if (!lmac->init_pend)
|
|
cgx_lmac_rx_tx_enable(lmac, lmac->lmac_id, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void nix_get_cgx_lmac_id(struct udevice *dev, int *cgxid, int *lmacid)
|
|
{
|
|
struct rvu_pf *rvu = dev_get_priv(dev);
|
|
struct nix *nix = rvu->nix;
|
|
struct lmac *lmac = nix->lmac;
|
|
|
|
*cgxid = lmac->cgx->cgx_id;
|
|
*lmacid = lmac->lmac_id;
|
|
}
|
|
|
|
void nix_print_mac_info(struct udevice *dev)
|
|
{
|
|
struct rvu_pf *rvu = dev_get_priv(dev);
|
|
struct nix *nix = rvu->nix;
|
|
struct lmac *lmac = nix->lmac;
|
|
|
|
printf(" CGX%d LMAC%d [%s]", lmac->cgx->cgx_id, lmac->lmac_id,
|
|
lmac_type_to_str[lmac->lmac_type]);
|
|
}
|
|
|