blob: 1acf112fc15af1fb6582dd3e043ea2a77b57f249 [file] [edit]
/*
* Broadcom NetXtreme-E User Space RoCE driver
*
* Copyright (c) 2015-2017, Broadcom. All rights reserved. The term
* Broadcom refers to Broadcom Limited and/or its subsidiaries.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* BSD license below:
*
* 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.
*
* Description: Doorbell handling functions.
*/
#include <util/mmio.h>
#include <ccan/minmax.h>
#include <stdlib.h>
#include "main.h"
static uint16_t rnd(struct xorshift32_state *state, uint16_t range)
{
/* range must be a power of 2 - 1 */
return (xorshift32(state) & range);
}
static int calculate_fifo_occupancy(struct bnxt_re_context *cntx)
{
struct bnxt_re_pacing_data *pacing_data =
(struct bnxt_re_pacing_data *)cntx->dbr_page;
struct bnxt_re_dev *rdev = cntx->rdev;
uint32_t read_val, fifo_occup;
uint64_t fifo_reg_off;
uint32_t *dbr_map;
fifo_reg_off = pacing_data->grc_reg_offset & ~(BNXT_RE_PAGE_MASK(rdev->pg_size));
dbr_map = cntx->bar_map + fifo_reg_off;
read_val = *dbr_map;
fifo_occup = pacing_data->fifo_max_depth -
((read_val & pacing_data->fifo_room_mask) >>
pacing_data->fifo_room_shift);
return fifo_occup;
}
static void bnxt_re_do_pacing(struct bnxt_re_context *cntx, struct xorshift32_state *state)
{
struct bnxt_re_pacing_data *pacing_data =
(struct bnxt_re_pacing_data *)cntx->dbr_page;
uint32_t fifo_occup;
int wait_time = 1;
if (!pacing_data)
return;
if (rnd(state, BNXT_RE_MAX_DO_PACING) < pacing_data->do_pacing) {
while ((fifo_occup = calculate_fifo_occupancy(cntx))
> pacing_data->pacing_th) {
uint32_t usec_wait;
if (pacing_data->alarm_th && fifo_occup > pacing_data->alarm_th)
bnxt_re_notify_drv(&cntx->ibvctx.context);
usec_wait = rnd(state, wait_time - 1);
if (usec_wait)
bnxt_re_sub_sec_busy_wait(usec_wait * 1000);
/* wait time capped at 128 us */
wait_time = min(wait_time * 2, 128);
}
}
}
static void bnxt_re_ring_db(struct bnxt_re_dpi *dpi,
struct bnxt_re_db_hdr *hdr)
{
__le64 *dbval;
dbval = (__le64 *)&hdr->indx;
mmio_wc_start();
mmio_write64_le(dpi->dbpage, *dbval);
mmio_flush_writes();
}
static void bnxt_re_init_db_hdr(struct bnxt_re_db_hdr *hdr, uint32_t indx,
uint32_t qid, uint32_t typ)
{
hdr->indx = htole32(indx & BNXT_RE_DB_INDX_MASK);
hdr->typ_qid = htole32(qid & BNXT_RE_DB_QID_MASK);
hdr->typ_qid |= htole32(((typ & BNXT_RE_DB_TYP_MASK) <<
BNXT_RE_DB_TYP_SHIFT));
}
void bnxt_re_ring_rq_db(struct bnxt_re_qp *qp)
{
struct bnxt_re_db_hdr hdr;
uint32_t tail;
bnxt_re_do_pacing(qp->cntx, &qp->rand);
tail = *qp->jrqq->hwque->dbtail;
bnxt_re_init_db_hdr(&hdr, tail, qp->qpid, BNXT_RE_QUE_TYPE_RQ);
bnxt_re_ring_db(qp->udpi, &hdr);
}
void bnxt_re_ring_sq_db(struct bnxt_re_qp *qp)
{
struct bnxt_re_db_hdr hdr;
uint32_t tail;
bnxt_re_do_pacing(qp->cntx, &qp->rand);
tail = *qp->jsqq->hwque->dbtail;
bnxt_re_init_db_hdr(&hdr, tail, qp->qpid, BNXT_RE_QUE_TYPE_SQ);
bnxt_re_ring_db(qp->udpi, &hdr);
}
void bnxt_re_ring_srq_db(struct bnxt_re_srq *srq)
{
struct bnxt_re_db_hdr hdr;
bnxt_re_do_pacing(srq->cntx, &srq->rand);
bnxt_re_init_db_hdr(&hdr, srq->srqq->tail, srq->srqid,
BNXT_RE_QUE_TYPE_SRQ);
bnxt_re_ring_db(srq->udpi, &hdr);
}
void bnxt_re_ring_srq_arm(struct bnxt_re_srq *srq)
{
struct bnxt_re_db_hdr hdr;
bnxt_re_do_pacing(srq->cntx, &srq->rand);
bnxt_re_init_db_hdr(&hdr, srq->cap.srq_limit, srq->srqid,
BNXT_RE_QUE_TYPE_SRQ_ARM);
bnxt_re_ring_db(srq->udpi, &hdr);
}
void bnxt_re_ring_cq_db(struct bnxt_re_cq *cq)
{
struct bnxt_re_db_hdr hdr;
bnxt_re_do_pacing(cq->cntx, &cq->rand);
bnxt_re_init_db_hdr(&hdr, cq->cqq.head, cq->cqid, BNXT_RE_QUE_TYPE_CQ);
bnxt_re_ring_db(cq->udpi, &hdr);
}
void bnxt_re_ring_cq_arm_db(struct bnxt_re_cq *cq, uint8_t aflag)
{
struct bnxt_re_db_hdr hdr;
bnxt_re_do_pacing(cq->cntx, &cq->rand);
bnxt_re_init_db_hdr(&hdr, cq->cqq.head, cq->cqid, aflag);
bnxt_re_ring_db(cq->udpi, &hdr);
}
void bnxt_re_ring_pstart_db(struct bnxt_re_qp *qp,
struct bnxt_re_push_buffer *pbuf)
{
uint64_t key;
bnxt_re_do_pacing(qp->cntx, &qp->rand);
key = ((((pbuf->wcdpi & BNXT_RE_DB_PIHI_MASK) <<
BNXT_RE_DB_PIHI_SHIFT) | (pbuf->qpid & BNXT_RE_DB_QID_MASK)) |
((BNXT_RE_PUSH_TYPE_START & BNXT_RE_DB_TYP_MASK) <<
BNXT_RE_DB_TYP_SHIFT) | (0x1UL << BNXT_RE_DB_VALID_SHIFT));
key <<= 32;
key |= ((((__u32)pbuf->wcdpi & BNXT_RE_DB_PILO_MASK) <<
BNXT_RE_DB_PILO_SHIFT) | (pbuf->st_idx &
BNXT_RE_DB_INDX_MASK));
udma_to_device_barrier();
mmio_write64((uintptr_t *)pbuf->ucdb, key);
}
void bnxt_re_ring_pend_db(struct bnxt_re_qp *qp,
struct bnxt_re_push_buffer *pbuf)
{
uint64_t key;
bnxt_re_do_pacing(qp->cntx, &qp->rand);
key = ((((pbuf->wcdpi & BNXT_RE_DB_PIHI_MASK) <<
BNXT_RE_DB_PIHI_SHIFT) | (pbuf->qpid & BNXT_RE_DB_QID_MASK)) |
((BNXT_RE_PUSH_TYPE_END & BNXT_RE_DB_TYP_MASK) <<
BNXT_RE_DB_TYP_SHIFT) | (0x1UL << BNXT_RE_DB_VALID_SHIFT));
key <<= 32;
key |= ((((__u32)pbuf->wcdpi & BNXT_RE_DB_PILO_MASK) <<
BNXT_RE_DB_PILO_SHIFT) | (pbuf->tail &
BNXT_RE_DB_INDX_MASK));
udma_to_device_barrier();
mmio_write64((uintptr_t *)pbuf->ucdb, key);
}
void bnxt_re_fill_push_wcb(struct bnxt_re_qp *qp,
struct bnxt_re_push_buffer *pbuf, uint32_t idx)
{
bnxt_re_ring_pstart_db(qp, pbuf);
mmio_wc_start();
bnxt_re_copy_data_to_pb(pbuf, 0, idx);
/* Flush WQE write before push end db. */
mmio_flush_writes();
bnxt_re_ring_pend_db(qp, pbuf);
}
int bnxt_re_init_pbuf_list(struct bnxt_re_context *ucntx)
{
struct bnxt_re_push_buffer *pbuf;
int indx, wqesz;
int size, offt;
uint64_t wcpage;
uint64_t dbpage;
void *base;
size = (sizeof(*ucntx->pbrec) +
16 * (sizeof(*ucntx->pbrec->pbuf) +
sizeof(struct bnxt_re_push_wqe)));
ucntx->pbrec = calloc(1, size);
if (!ucntx->pbrec)
goto out;
offt = sizeof(*ucntx->pbrec);
base = ucntx->pbrec;
ucntx->pbrec->pbuf = (base + offt);
ucntx->pbrec->pbmap = ~0x00;
ucntx->pbrec->pbmap &= ~0x7fff; /* 15 bits */
ucntx->pbrec->udpi = &ucntx->udpi;
wqesz = sizeof(struct bnxt_re_push_wqe);
wcpage = (uintptr_t)(ucntx->udpi.wcdbpg);
dbpage = (uintptr_t)(ucntx->udpi.dbpage);
offt = sizeof(*ucntx->pbrec->pbuf) * 16;
base = (char *)ucntx->pbrec->pbuf + offt;
for (indx = 0; indx < 16; indx++) {
pbuf = &ucntx->pbrec->pbuf[indx];
pbuf->wqe = base + indx * wqesz;
pbuf->pbuf = (uintptr_t)(wcpage + indx * wqesz);
pbuf->ucdb = (uintptr_t)(dbpage + (indx + 1) * sizeof(uint64_t));
pbuf->wcdpi = ucntx->udpi.wcdpi;
}
return 0;
out:
return -ENOMEM;
}
struct bnxt_re_push_buffer *bnxt_re_get_pbuf(uint8_t *push_st_en,
struct bnxt_re_context *cntx)
{
struct bnxt_re_push_buffer *pbuf = NULL;
__u32 old;
int bit;
old = cntx->pbrec->pbmap;
while ((bit = __builtin_ffs(~cntx->pbrec->pbmap)) != 0) {
if (__sync_bool_compare_and_swap
(&cntx->pbrec->pbmap,
old,
(old | 0x01 << (bit - 1))))
break;
old = cntx->pbrec->pbmap;
}
if (bit) {
pbuf = &cntx->pbrec->pbuf[bit];
pbuf->nbit = bit;
}
return pbuf;
}
void bnxt_re_put_pbuf(struct bnxt_re_context *cntx,
struct bnxt_re_push_buffer *pbuf)
{
struct bnxt_re_push_rec *pbrec;
__u32 old;
int bit;
pbrec = cntx->pbrec;
if (pbuf->nbit) {
bit = pbuf->nbit;
pbuf->nbit = 0;
old = pbrec->pbmap;
while (!__sync_bool_compare_and_swap(&pbrec->pbmap, old,
(old & (~(0x01 <<
(bit - 1))))))
old = pbrec->pbmap;
}
}
void bnxt_re_destroy_pbuf_list(struct bnxt_re_context *cntx)
{
free(cntx->pbrec);
}