162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is part of the Chelsio FCoE driver for Linux.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This software is available to you under a choice of one of two
762306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
862306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
962306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
1062306a36Sopenharmony_ci * OpenIB.org BSD license below:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1362306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1462306a36Sopenharmony_ci *     conditions are met:
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1762306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1862306a36Sopenharmony_ci *        disclaimer.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
2162306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2262306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2362306a36Sopenharmony_ci *        provided with the distribution.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2662306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2762306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2862306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2962306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
3062306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3162306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3262306a36Sopenharmony_ci * SOFTWARE.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/kernel.h>
3662306a36Sopenharmony_ci#include <linux/string.h>
3762306a36Sopenharmony_ci#include <linux/compiler.h>
3862306a36Sopenharmony_ci#include <linux/slab.h>
3962306a36Sopenharmony_ci#include <asm/page.h>
4062306a36Sopenharmony_ci#include <linux/cache.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include "t4_values.h"
4362306a36Sopenharmony_ci#include "csio_hw.h"
4462306a36Sopenharmony_ci#include "csio_wr.h"
4562306a36Sopenharmony_ci#include "csio_mb.h"
4662306a36Sopenharmony_ci#include "csio_defs.h"
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciint csio_intr_coalesce_cnt;		/* value:SGE_INGRESS_RX_THRESHOLD[0] */
4962306a36Sopenharmony_cistatic int csio_sge_thresh_reg;		/* SGE_INGRESS_RX_THRESHOLD[0] */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciint csio_intr_coalesce_time = 10;	/* value:SGE_TIMER_VALUE_1 */
5262306a36Sopenharmony_cistatic int csio_sge_timer_reg = 1;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define CSIO_SET_FLBUF_SIZE(_hw, _reg, _val)				\
5562306a36Sopenharmony_ci	csio_wr_reg32((_hw), (_val), SGE_FL_BUFFER_SIZE##_reg##_A)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void
5862306a36Sopenharmony_cicsio_get_flbuf_size(struct csio_hw *hw, struct csio_sge *sge, uint32_t reg)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	sge->sge_fl_buf_size[reg] = csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE0_A +
6162306a36Sopenharmony_ci							reg * sizeof(uint32_t));
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Free list buffer size */
6562306a36Sopenharmony_cistatic inline uint32_t
6662306a36Sopenharmony_cicsio_wr_fl_bufsz(struct csio_sge *sge, struct csio_dma_buf *buf)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	return sge->sge_fl_buf_size[buf->paddr & 0xF];
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* Size of the egress queue status page */
7262306a36Sopenharmony_cistatic inline uint32_t
7362306a36Sopenharmony_cicsio_wr_qstat_pgsz(struct csio_hw *hw)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	return (hw->wrm.sge.sge_control & EGRSTATUSPAGESIZE_F) ?  128 : 64;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* Ring freelist doorbell */
7962306a36Sopenharmony_cistatic inline void
8062306a36Sopenharmony_cicsio_wr_ring_fldb(struct csio_hw *hw, struct csio_q *flq)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	/*
8362306a36Sopenharmony_ci	 * Ring the doorbell only when we have atleast CSIO_QCREDIT_SZ
8462306a36Sopenharmony_ci	 * number of bytes in the freelist queue. This translates to atleast
8562306a36Sopenharmony_ci	 * 8 freelist buffer pointers (since each pointer is 8 bytes).
8662306a36Sopenharmony_ci	 */
8762306a36Sopenharmony_ci	if (flq->inc_idx >= 8) {
8862306a36Sopenharmony_ci		csio_wr_reg32(hw, DBPRIO_F | QID_V(flq->un.fl.flid) |
8962306a36Sopenharmony_ci				  PIDX_T5_V(flq->inc_idx / 8) | DBTYPE_F,
9062306a36Sopenharmony_ci				  MYPF_REG(SGE_PF_KDOORBELL_A));
9162306a36Sopenharmony_ci		flq->inc_idx &= 7;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/* Write a 0 cidx increment value to enable SGE interrupts for this queue */
9662306a36Sopenharmony_cistatic void
9762306a36Sopenharmony_cicsio_wr_sge_intr_enable(struct csio_hw *hw, uint16_t iqid)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	csio_wr_reg32(hw, CIDXINC_V(0)		|
10062306a36Sopenharmony_ci			  INGRESSQID_V(iqid)	|
10162306a36Sopenharmony_ci			  TIMERREG_V(X_TIMERREG_RESTART_COUNTER),
10262306a36Sopenharmony_ci			  MYPF_REG(SGE_PF_GTS_A));
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/*
10662306a36Sopenharmony_ci * csio_wr_fill_fl - Populate the FL buffers of a FL queue.
10762306a36Sopenharmony_ci * @hw: HW module.
10862306a36Sopenharmony_ci * @flq: Freelist queue.
10962306a36Sopenharmony_ci *
11062306a36Sopenharmony_ci * Fill up freelist buffer entries with buffers of size specified
11162306a36Sopenharmony_ci * in the size register.
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_cistatic int
11562306a36Sopenharmony_cicsio_wr_fill_fl(struct csio_hw *hw, struct csio_q *flq)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct csio_wrm *wrm = csio_hw_to_wrm(hw);
11862306a36Sopenharmony_ci	struct csio_sge *sge = &wrm->sge;
11962306a36Sopenharmony_ci	__be64 *d = (__be64 *)(flq->vstart);
12062306a36Sopenharmony_ci	struct csio_dma_buf *buf = &flq->un.fl.bufs[0];
12162306a36Sopenharmony_ci	uint64_t paddr;
12262306a36Sopenharmony_ci	int sreg = flq->un.fl.sreg;
12362306a36Sopenharmony_ci	int n = flq->credits;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	while (n--) {
12662306a36Sopenharmony_ci		buf->len = sge->sge_fl_buf_size[sreg];
12762306a36Sopenharmony_ci		buf->vaddr = dma_alloc_coherent(&hw->pdev->dev, buf->len,
12862306a36Sopenharmony_ci						&buf->paddr, GFP_KERNEL);
12962306a36Sopenharmony_ci		if (!buf->vaddr) {
13062306a36Sopenharmony_ci			csio_err(hw, "Could only fill %d buffers!\n", n + 1);
13162306a36Sopenharmony_ci			return -ENOMEM;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		paddr = buf->paddr | (sreg & 0xF);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		*d++ = cpu_to_be64(paddr);
13762306a36Sopenharmony_ci		buf++;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return 0;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * csio_wr_update_fl -
14562306a36Sopenharmony_ci * @hw: HW module.
14662306a36Sopenharmony_ci * @flq: Freelist queue.
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_cistatic inline void
15162306a36Sopenharmony_cicsio_wr_update_fl(struct csio_hw *hw, struct csio_q *flq, uint16_t n)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	flq->inc_idx += n;
15562306a36Sopenharmony_ci	flq->pidx += n;
15662306a36Sopenharmony_ci	if (unlikely(flq->pidx >= flq->credits))
15762306a36Sopenharmony_ci		flq->pidx -= (uint16_t)flq->credits;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	CSIO_INC_STATS(flq, n_flq_refill);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/*
16362306a36Sopenharmony_ci * csio_wr_alloc_q - Allocate a WR queue and initialize it.
16462306a36Sopenharmony_ci * @hw: HW module
16562306a36Sopenharmony_ci * @qsize: Size of the queue in bytes
16662306a36Sopenharmony_ci * @wrsize: Since of WR in this queue, if fixed.
16762306a36Sopenharmony_ci * @type: Type of queue (Ingress/Egress/Freelist)
16862306a36Sopenharmony_ci * @owner: Module that owns this queue.
16962306a36Sopenharmony_ci * @nflb: Number of freelist buffers for FL.
17062306a36Sopenharmony_ci * @sreg: What is the FL buffer size register?
17162306a36Sopenharmony_ci * @iq_int_handler: Ingress queue handler in INTx mode.
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci * This function allocates and sets up a queue for the caller
17462306a36Sopenharmony_ci * of size qsize, aligned at the required boundary. This is subject to
17562306a36Sopenharmony_ci * be free entries being available in the queue array. If one is found,
17662306a36Sopenharmony_ci * it is initialized with the allocated queue, marked as being used (owner),
17762306a36Sopenharmony_ci * and a handle returned to the caller in form of the queue's index
17862306a36Sopenharmony_ci * into the q_arr array.
17962306a36Sopenharmony_ci * If user has indicated a freelist (by specifying nflb > 0), create
18062306a36Sopenharmony_ci * another queue (with its own index into q_arr) for the freelist. Allocate
18162306a36Sopenharmony_ci * memory for DMA buffer metadata (vaddr, len etc). Save off the freelist
18262306a36Sopenharmony_ci * idx in the ingress queue's flq.idx. This is how a Freelist is associated
18362306a36Sopenharmony_ci * with its owning ingress queue.
18462306a36Sopenharmony_ci */
18562306a36Sopenharmony_ciint
18662306a36Sopenharmony_cicsio_wr_alloc_q(struct csio_hw *hw, uint32_t qsize, uint32_t wrsize,
18762306a36Sopenharmony_ci		uint16_t type, void *owner, uint32_t nflb, int sreg,
18862306a36Sopenharmony_ci		iq_handler_t iq_intx_handler)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct csio_wrm *wrm = csio_hw_to_wrm(hw);
19162306a36Sopenharmony_ci	struct csio_q	*q, *flq;
19262306a36Sopenharmony_ci	int		free_idx = wrm->free_qidx;
19362306a36Sopenharmony_ci	int		ret_idx = free_idx;
19462306a36Sopenharmony_ci	uint32_t	qsz;
19562306a36Sopenharmony_ci	int flq_idx;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (free_idx >= wrm->num_q) {
19862306a36Sopenharmony_ci		csio_err(hw, "No more free queues.\n");
19962306a36Sopenharmony_ci		return -1;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	switch (type) {
20362306a36Sopenharmony_ci	case CSIO_EGRESS:
20462306a36Sopenharmony_ci		qsz = ALIGN(qsize, CSIO_QCREDIT_SZ) + csio_wr_qstat_pgsz(hw);
20562306a36Sopenharmony_ci		break;
20662306a36Sopenharmony_ci	case CSIO_INGRESS:
20762306a36Sopenharmony_ci		switch (wrsize) {
20862306a36Sopenharmony_ci		case 16:
20962306a36Sopenharmony_ci		case 32:
21062306a36Sopenharmony_ci		case 64:
21162306a36Sopenharmony_ci		case 128:
21262306a36Sopenharmony_ci			break;
21362306a36Sopenharmony_ci		default:
21462306a36Sopenharmony_ci			csio_err(hw, "Invalid Ingress queue WR size:%d\n",
21562306a36Sopenharmony_ci				    wrsize);
21662306a36Sopenharmony_ci			return -1;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		/*
22062306a36Sopenharmony_ci		 * Number of elements must be a multiple of 16
22162306a36Sopenharmony_ci		 * So this includes status page size
22262306a36Sopenharmony_ci		 */
22362306a36Sopenharmony_ci		qsz = ALIGN(qsize/wrsize, 16) * wrsize;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		break;
22662306a36Sopenharmony_ci	case CSIO_FREELIST:
22762306a36Sopenharmony_ci		qsz = ALIGN(qsize/wrsize, 8) * wrsize + csio_wr_qstat_pgsz(hw);
22862306a36Sopenharmony_ci		break;
22962306a36Sopenharmony_ci	default:
23062306a36Sopenharmony_ci		csio_err(hw, "Invalid queue type: 0x%x\n", type);
23162306a36Sopenharmony_ci		return -1;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	q = wrm->q_arr[free_idx];
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	q->vstart = dma_alloc_coherent(&hw->pdev->dev, qsz, &q->pstart,
23762306a36Sopenharmony_ci				       GFP_KERNEL);
23862306a36Sopenharmony_ci	if (!q->vstart) {
23962306a36Sopenharmony_ci		csio_err(hw,
24062306a36Sopenharmony_ci			 "Failed to allocate DMA memory for "
24162306a36Sopenharmony_ci			 "queue at id: %d size: %d\n", free_idx, qsize);
24262306a36Sopenharmony_ci		return -1;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	q->type		= type;
24662306a36Sopenharmony_ci	q->owner	= owner;
24762306a36Sopenharmony_ci	q->pidx		= q->cidx = q->inc_idx = 0;
24862306a36Sopenharmony_ci	q->size		= qsz;
24962306a36Sopenharmony_ci	q->wr_sz	= wrsize;	/* If using fixed size WRs */
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	wrm->free_qidx++;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (type == CSIO_INGRESS) {
25462306a36Sopenharmony_ci		/* Since queue area is set to zero */
25562306a36Sopenharmony_ci		q->un.iq.genbit	= 1;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		/*
25862306a36Sopenharmony_ci		 * Ingress queue status page size is always the size of
25962306a36Sopenharmony_ci		 * the ingress queue entry.
26062306a36Sopenharmony_ci		 */
26162306a36Sopenharmony_ci		q->credits	= (qsz - q->wr_sz) / q->wr_sz;
26262306a36Sopenharmony_ci		q->vwrap	= (void *)((uintptr_t)(q->vstart) + qsz
26362306a36Sopenharmony_ci							- q->wr_sz);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci		/* Allocate memory for FL if requested */
26662306a36Sopenharmony_ci		if (nflb > 0) {
26762306a36Sopenharmony_ci			flq_idx = csio_wr_alloc_q(hw, nflb * sizeof(__be64),
26862306a36Sopenharmony_ci						  sizeof(__be64), CSIO_FREELIST,
26962306a36Sopenharmony_ci						  owner, 0, sreg, NULL);
27062306a36Sopenharmony_ci			if (flq_idx == -1) {
27162306a36Sopenharmony_ci				csio_err(hw,
27262306a36Sopenharmony_ci					 "Failed to allocate FL queue"
27362306a36Sopenharmony_ci					 " for IQ idx:%d\n", free_idx);
27462306a36Sopenharmony_ci				return -1;
27562306a36Sopenharmony_ci			}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci			/* Associate the new FL with the Ingress quue */
27862306a36Sopenharmony_ci			q->un.iq.flq_idx = flq_idx;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci			flq = wrm->q_arr[q->un.iq.flq_idx];
28162306a36Sopenharmony_ci			flq->un.fl.bufs = kcalloc(flq->credits,
28262306a36Sopenharmony_ci						  sizeof(struct csio_dma_buf),
28362306a36Sopenharmony_ci						  GFP_KERNEL);
28462306a36Sopenharmony_ci			if (!flq->un.fl.bufs) {
28562306a36Sopenharmony_ci				csio_err(hw,
28662306a36Sopenharmony_ci					 "Failed to allocate FL queue bufs"
28762306a36Sopenharmony_ci					 " for IQ idx:%d\n", free_idx);
28862306a36Sopenharmony_ci				return -1;
28962306a36Sopenharmony_ci			}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci			flq->un.fl.packen = 0;
29262306a36Sopenharmony_ci			flq->un.fl.offset = 0;
29362306a36Sopenharmony_ci			flq->un.fl.sreg = sreg;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci			/* Fill up the free list buffers */
29662306a36Sopenharmony_ci			if (csio_wr_fill_fl(hw, flq))
29762306a36Sopenharmony_ci				return -1;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci			/*
30062306a36Sopenharmony_ci			 * Make sure in a FLQ, atleast 1 credit (8 FL buffers)
30162306a36Sopenharmony_ci			 * remains unpopulated,otherwise HW thinks
30262306a36Sopenharmony_ci			 * FLQ is empty.
30362306a36Sopenharmony_ci			 */
30462306a36Sopenharmony_ci			flq->pidx = flq->inc_idx = flq->credits - 8;
30562306a36Sopenharmony_ci		} else {
30662306a36Sopenharmony_ci			q->un.iq.flq_idx = -1;
30762306a36Sopenharmony_ci		}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		/* Associate the IQ INTx handler. */
31062306a36Sopenharmony_ci		q->un.iq.iq_intx_handler = iq_intx_handler;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		csio_q_iqid(hw, ret_idx) = CSIO_MAX_QID;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	} else if (type == CSIO_EGRESS) {
31562306a36Sopenharmony_ci		q->credits = (qsz - csio_wr_qstat_pgsz(hw)) / CSIO_QCREDIT_SZ;
31662306a36Sopenharmony_ci		q->vwrap   = (void *)((uintptr_t)(q->vstart) + qsz
31762306a36Sopenharmony_ci						- csio_wr_qstat_pgsz(hw));
31862306a36Sopenharmony_ci		csio_q_eqid(hw, ret_idx) = CSIO_MAX_QID;
31962306a36Sopenharmony_ci	} else { /* Freelist */
32062306a36Sopenharmony_ci		q->credits = (qsz - csio_wr_qstat_pgsz(hw)) / sizeof(__be64);
32162306a36Sopenharmony_ci		q->vwrap   = (void *)((uintptr_t)(q->vstart) + qsz
32262306a36Sopenharmony_ci						- csio_wr_qstat_pgsz(hw));
32362306a36Sopenharmony_ci		csio_q_flid(hw, ret_idx) = CSIO_MAX_QID;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return ret_idx;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/*
33062306a36Sopenharmony_ci * csio_wr_iq_create_rsp - Response handler for IQ creation.
33162306a36Sopenharmony_ci * @hw: The HW module.
33262306a36Sopenharmony_ci * @mbp: Mailbox.
33362306a36Sopenharmony_ci * @iq_idx: Ingress queue that got created.
33462306a36Sopenharmony_ci *
33562306a36Sopenharmony_ci * Handle FW_IQ_CMD mailbox completion. Save off the assigned IQ/FL ids.
33662306a36Sopenharmony_ci */
33762306a36Sopenharmony_cistatic int
33862306a36Sopenharmony_cicsio_wr_iq_create_rsp(struct csio_hw *hw, struct csio_mb *mbp, int iq_idx)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct csio_iq_params iqp;
34162306a36Sopenharmony_ci	enum fw_retval retval;
34262306a36Sopenharmony_ci	uint32_t iq_id;
34362306a36Sopenharmony_ci	int flq_idx;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	memset(&iqp, 0, sizeof(struct csio_iq_params));
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	csio_mb_iq_alloc_write_rsp(hw, mbp, &retval, &iqp);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (retval != FW_SUCCESS) {
35062306a36Sopenharmony_ci		csio_err(hw, "IQ cmd returned 0x%x!\n", retval);
35162306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
35262306a36Sopenharmony_ci		return -EINVAL;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	csio_q_iqid(hw, iq_idx)		= iqp.iqid;
35662306a36Sopenharmony_ci	csio_q_physiqid(hw, iq_idx)	= iqp.physiqid;
35762306a36Sopenharmony_ci	csio_q_pidx(hw, iq_idx)		= csio_q_cidx(hw, iq_idx) = 0;
35862306a36Sopenharmony_ci	csio_q_inc_idx(hw, iq_idx)	= 0;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* Actual iq-id. */
36162306a36Sopenharmony_ci	iq_id = iqp.iqid - hw->wrm.fw_iq_start;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	/* Set the iq-id to iq map table. */
36462306a36Sopenharmony_ci	if (iq_id >= CSIO_MAX_IQ) {
36562306a36Sopenharmony_ci		csio_err(hw,
36662306a36Sopenharmony_ci			 "Exceeding MAX_IQ(%d) supported!"
36762306a36Sopenharmony_ci			 " iqid:%d rel_iqid:%d FW iq_start:%d\n",
36862306a36Sopenharmony_ci			 CSIO_MAX_IQ, iq_id, iqp.iqid, hw->wrm.fw_iq_start);
36962306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
37062306a36Sopenharmony_ci		return -EINVAL;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci	csio_q_set_intr_map(hw, iq_idx, iq_id);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/*
37562306a36Sopenharmony_ci	 * During FW_IQ_CMD, FW sets interrupt_sent bit to 1 in the SGE
37662306a36Sopenharmony_ci	 * ingress context of this queue. This will block interrupts to
37762306a36Sopenharmony_ci	 * this queue until the next GTS write. Therefore, we do a
37862306a36Sopenharmony_ci	 * 0-cidx increment GTS write for this queue just to clear the
37962306a36Sopenharmony_ci	 * interrupt_sent bit. This will re-enable interrupts to this
38062306a36Sopenharmony_ci	 * queue.
38162306a36Sopenharmony_ci	 */
38262306a36Sopenharmony_ci	csio_wr_sge_intr_enable(hw, iqp.physiqid);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	flq_idx = csio_q_iq_flq_idx(hw, iq_idx);
38562306a36Sopenharmony_ci	if (flq_idx != -1) {
38662306a36Sopenharmony_ci		struct csio_q *flq = hw->wrm.q_arr[flq_idx];
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		csio_q_flid(hw, flq_idx) = iqp.fl0id;
38962306a36Sopenharmony_ci		csio_q_cidx(hw, flq_idx) = 0;
39062306a36Sopenharmony_ci		csio_q_pidx(hw, flq_idx)    = csio_q_credits(hw, flq_idx) - 8;
39162306a36Sopenharmony_ci		csio_q_inc_idx(hw, flq_idx) = csio_q_credits(hw, flq_idx) - 8;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		/* Now update SGE about the buffers allocated during init */
39462306a36Sopenharmony_ci		csio_wr_ring_fldb(hw, flq);
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return 0;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci/*
40362306a36Sopenharmony_ci * csio_wr_iq_create - Configure an Ingress queue with FW.
40462306a36Sopenharmony_ci * @hw: The HW module.
40562306a36Sopenharmony_ci * @priv: Private data object.
40662306a36Sopenharmony_ci * @iq_idx: Ingress queue index in the WR module.
40762306a36Sopenharmony_ci * @vec: MSIX vector.
40862306a36Sopenharmony_ci * @portid: PCIE Channel to be associated with this queue.
40962306a36Sopenharmony_ci * @async: Is this a FW asynchronous message handling queue?
41062306a36Sopenharmony_ci * @cbfn: Completion callback.
41162306a36Sopenharmony_ci *
41262306a36Sopenharmony_ci * This API configures an ingress queue with FW by issuing a FW_IQ_CMD mailbox
41362306a36Sopenharmony_ci * with alloc/write bits set.
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_ciint
41662306a36Sopenharmony_cicsio_wr_iq_create(struct csio_hw *hw, void *priv, int iq_idx,
41762306a36Sopenharmony_ci		  uint32_t vec, uint8_t portid, bool async,
41862306a36Sopenharmony_ci		  void (*cbfn) (struct csio_hw *, struct csio_mb *))
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct csio_mb  *mbp;
42162306a36Sopenharmony_ci	struct csio_iq_params iqp;
42262306a36Sopenharmony_ci	int flq_idx;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	memset(&iqp, 0, sizeof(struct csio_iq_params));
42562306a36Sopenharmony_ci	csio_q_portid(hw, iq_idx) = portid;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
42862306a36Sopenharmony_ci	if (!mbp) {
42962306a36Sopenharmony_ci		csio_err(hw, "IQ command out of memory!\n");
43062306a36Sopenharmony_ci		return -ENOMEM;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	switch (hw->intr_mode) {
43462306a36Sopenharmony_ci	case CSIO_IM_INTX:
43562306a36Sopenharmony_ci	case CSIO_IM_MSI:
43662306a36Sopenharmony_ci		/* For interrupt forwarding queue only */
43762306a36Sopenharmony_ci		if (hw->intr_iq_idx == iq_idx)
43862306a36Sopenharmony_ci			iqp.iqandst	= X_INTERRUPTDESTINATION_PCIE;
43962306a36Sopenharmony_ci		else
44062306a36Sopenharmony_ci			iqp.iqandst	= X_INTERRUPTDESTINATION_IQ;
44162306a36Sopenharmony_ci		iqp.iqandstindex	=
44262306a36Sopenharmony_ci			csio_q_physiqid(hw, hw->intr_iq_idx);
44362306a36Sopenharmony_ci		break;
44462306a36Sopenharmony_ci	case CSIO_IM_MSIX:
44562306a36Sopenharmony_ci		iqp.iqandst		= X_INTERRUPTDESTINATION_PCIE;
44662306a36Sopenharmony_ci		iqp.iqandstindex	= (uint16_t)vec;
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci	case CSIO_IM_NONE:
44962306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
45062306a36Sopenharmony_ci		return -EINVAL;
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/* Pass in the ingress queue cmd parameters */
45462306a36Sopenharmony_ci	iqp.pfn			= hw->pfn;
45562306a36Sopenharmony_ci	iqp.vfn			= 0;
45662306a36Sopenharmony_ci	iqp.iq_start		= 1;
45762306a36Sopenharmony_ci	iqp.viid		= 0;
45862306a36Sopenharmony_ci	iqp.type		= FW_IQ_TYPE_FL_INT_CAP;
45962306a36Sopenharmony_ci	iqp.iqasynch		= async;
46062306a36Sopenharmony_ci	if (csio_intr_coalesce_cnt)
46162306a36Sopenharmony_ci		iqp.iqanus	= X_UPDATESCHEDULING_COUNTER_OPTTIMER;
46262306a36Sopenharmony_ci	else
46362306a36Sopenharmony_ci		iqp.iqanus	= X_UPDATESCHEDULING_TIMER;
46462306a36Sopenharmony_ci	iqp.iqanud		= X_UPDATEDELIVERY_INTERRUPT;
46562306a36Sopenharmony_ci	iqp.iqpciech		= portid;
46662306a36Sopenharmony_ci	iqp.iqintcntthresh	= (uint8_t)csio_sge_thresh_reg;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	switch (csio_q_wr_sz(hw, iq_idx)) {
46962306a36Sopenharmony_ci	case 16:
47062306a36Sopenharmony_ci		iqp.iqesize = 0; break;
47162306a36Sopenharmony_ci	case 32:
47262306a36Sopenharmony_ci		iqp.iqesize = 1; break;
47362306a36Sopenharmony_ci	case 64:
47462306a36Sopenharmony_ci		iqp.iqesize = 2; break;
47562306a36Sopenharmony_ci	case 128:
47662306a36Sopenharmony_ci		iqp.iqesize = 3; break;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	iqp.iqsize		= csio_q_size(hw, iq_idx) /
48062306a36Sopenharmony_ci						csio_q_wr_sz(hw, iq_idx);
48162306a36Sopenharmony_ci	iqp.iqaddr		= csio_q_pstart(hw, iq_idx);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	flq_idx = csio_q_iq_flq_idx(hw, iq_idx);
48462306a36Sopenharmony_ci	if (flq_idx != -1) {
48562306a36Sopenharmony_ci		enum chip_type chip = CHELSIO_CHIP_VERSION(hw->chip_id);
48662306a36Sopenharmony_ci		struct csio_q *flq = hw->wrm.q_arr[flq_idx];
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		iqp.fl0paden	= 1;
48962306a36Sopenharmony_ci		iqp.fl0packen	= flq->un.fl.packen ? 1 : 0;
49062306a36Sopenharmony_ci		iqp.fl0fbmin	= X_FETCHBURSTMIN_64B;
49162306a36Sopenharmony_ci		iqp.fl0fbmax	= ((chip == CHELSIO_T5) ?
49262306a36Sopenharmony_ci				  X_FETCHBURSTMAX_512B : X_FETCHBURSTMAX_256B);
49362306a36Sopenharmony_ci		iqp.fl0size	= csio_q_size(hw, flq_idx) / CSIO_QCREDIT_SZ;
49462306a36Sopenharmony_ci		iqp.fl0addr	= csio_q_pstart(hw, flq_idx);
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	csio_mb_iq_alloc_write(hw, mbp, priv, CSIO_MB_DEFAULT_TMO, &iqp, cbfn);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (csio_mb_issue(hw, mbp)) {
50062306a36Sopenharmony_ci		csio_err(hw, "Issue of IQ cmd failed!\n");
50162306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
50262306a36Sopenharmony_ci		return -EINVAL;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (cbfn != NULL)
50662306a36Sopenharmony_ci		return 0;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	return csio_wr_iq_create_rsp(hw, mbp, iq_idx);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci/*
51262306a36Sopenharmony_ci * csio_wr_eq_create_rsp - Response handler for EQ creation.
51362306a36Sopenharmony_ci * @hw: The HW module.
51462306a36Sopenharmony_ci * @mbp: Mailbox.
51562306a36Sopenharmony_ci * @eq_idx: Egress queue that got created.
51662306a36Sopenharmony_ci *
51762306a36Sopenharmony_ci * Handle FW_EQ_OFLD_CMD mailbox completion. Save off the assigned EQ ids.
51862306a36Sopenharmony_ci */
51962306a36Sopenharmony_cistatic int
52062306a36Sopenharmony_cicsio_wr_eq_cfg_rsp(struct csio_hw *hw, struct csio_mb *mbp, int eq_idx)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct csio_eq_params eqp;
52362306a36Sopenharmony_ci	enum fw_retval retval;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	memset(&eqp, 0, sizeof(struct csio_eq_params));
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	csio_mb_eq_ofld_alloc_write_rsp(hw, mbp, &retval, &eqp);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (retval != FW_SUCCESS) {
53062306a36Sopenharmony_ci		csio_err(hw, "EQ OFLD cmd returned 0x%x!\n", retval);
53162306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
53262306a36Sopenharmony_ci		return -EINVAL;
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	csio_q_eqid(hw, eq_idx)	= (uint16_t)eqp.eqid;
53662306a36Sopenharmony_ci	csio_q_physeqid(hw, eq_idx) = (uint16_t)eqp.physeqid;
53762306a36Sopenharmony_ci	csio_q_pidx(hw, eq_idx)	= csio_q_cidx(hw, eq_idx) = 0;
53862306a36Sopenharmony_ci	csio_q_inc_idx(hw, eq_idx) = 0;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return 0;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci/*
54662306a36Sopenharmony_ci * csio_wr_eq_create - Configure an Egress queue with FW.
54762306a36Sopenharmony_ci * @hw: HW module.
54862306a36Sopenharmony_ci * @priv: Private data.
54962306a36Sopenharmony_ci * @eq_idx: Egress queue index in the WR module.
55062306a36Sopenharmony_ci * @iq_idx: Associated ingress queue index.
55162306a36Sopenharmony_ci * @cbfn: Completion callback.
55262306a36Sopenharmony_ci *
55362306a36Sopenharmony_ci * This API configures a offload egress queue with FW by issuing a
55462306a36Sopenharmony_ci * FW_EQ_OFLD_CMD  (with alloc + write ) mailbox.
55562306a36Sopenharmony_ci */
55662306a36Sopenharmony_ciint
55762306a36Sopenharmony_cicsio_wr_eq_create(struct csio_hw *hw, void *priv, int eq_idx,
55862306a36Sopenharmony_ci		  int iq_idx, uint8_t portid,
55962306a36Sopenharmony_ci		  void (*cbfn) (struct csio_hw *, struct csio_mb *))
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct csio_mb  *mbp;
56262306a36Sopenharmony_ci	struct csio_eq_params eqp;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	memset(&eqp, 0, sizeof(struct csio_eq_params));
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
56762306a36Sopenharmony_ci	if (!mbp) {
56862306a36Sopenharmony_ci		csio_err(hw, "EQ command out of memory!\n");
56962306a36Sopenharmony_ci		return -ENOMEM;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	eqp.pfn			= hw->pfn;
57362306a36Sopenharmony_ci	eqp.vfn			= 0;
57462306a36Sopenharmony_ci	eqp.eqstart		= 1;
57562306a36Sopenharmony_ci	eqp.hostfcmode		= X_HOSTFCMODE_STATUS_PAGE;
57662306a36Sopenharmony_ci	eqp.iqid		= csio_q_iqid(hw, iq_idx);
57762306a36Sopenharmony_ci	eqp.fbmin		= X_FETCHBURSTMIN_64B;
57862306a36Sopenharmony_ci	eqp.fbmax		= X_FETCHBURSTMAX_512B;
57962306a36Sopenharmony_ci	eqp.cidxfthresh		= 0;
58062306a36Sopenharmony_ci	eqp.pciechn		= portid;
58162306a36Sopenharmony_ci	eqp.eqsize		= csio_q_size(hw, eq_idx) / CSIO_QCREDIT_SZ;
58262306a36Sopenharmony_ci	eqp.eqaddr		= csio_q_pstart(hw, eq_idx);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	csio_mb_eq_ofld_alloc_write(hw, mbp, priv, CSIO_MB_DEFAULT_TMO,
58562306a36Sopenharmony_ci				    &eqp, cbfn);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (csio_mb_issue(hw, mbp)) {
58862306a36Sopenharmony_ci		csio_err(hw, "Issue of EQ OFLD cmd failed!\n");
58962306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
59062306a36Sopenharmony_ci		return -EINVAL;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (cbfn != NULL)
59462306a36Sopenharmony_ci		return 0;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	return csio_wr_eq_cfg_rsp(hw, mbp, eq_idx);
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci/*
60062306a36Sopenharmony_ci * csio_wr_iq_destroy_rsp - Response handler for IQ removal.
60162306a36Sopenharmony_ci * @hw: The HW module.
60262306a36Sopenharmony_ci * @mbp: Mailbox.
60362306a36Sopenharmony_ci * @iq_idx: Ingress queue that was freed.
60462306a36Sopenharmony_ci *
60562306a36Sopenharmony_ci * Handle FW_IQ_CMD (free) mailbox completion.
60662306a36Sopenharmony_ci */
60762306a36Sopenharmony_cistatic int
60862306a36Sopenharmony_cicsio_wr_iq_destroy_rsp(struct csio_hw *hw, struct csio_mb *mbp, int iq_idx)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	enum fw_retval retval = csio_mb_fw_retval(mbp);
61162306a36Sopenharmony_ci	int rv = 0;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	if (retval != FW_SUCCESS)
61462306a36Sopenharmony_ci		rv = -EINVAL;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	return rv;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci/*
62262306a36Sopenharmony_ci * csio_wr_iq_destroy - Free an ingress queue.
62362306a36Sopenharmony_ci * @hw: The HW module.
62462306a36Sopenharmony_ci * @priv: Private data object.
62562306a36Sopenharmony_ci * @iq_idx: Ingress queue index to destroy
62662306a36Sopenharmony_ci * @cbfn: Completion callback.
62762306a36Sopenharmony_ci *
62862306a36Sopenharmony_ci * This API frees an ingress queue by issuing the FW_IQ_CMD
62962306a36Sopenharmony_ci * with the free bit set.
63062306a36Sopenharmony_ci */
63162306a36Sopenharmony_cistatic int
63262306a36Sopenharmony_cicsio_wr_iq_destroy(struct csio_hw *hw, void *priv, int iq_idx,
63362306a36Sopenharmony_ci		   void (*cbfn)(struct csio_hw *, struct csio_mb *))
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	int rv = 0;
63662306a36Sopenharmony_ci	struct csio_mb  *mbp;
63762306a36Sopenharmony_ci	struct csio_iq_params iqp;
63862306a36Sopenharmony_ci	int flq_idx;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	memset(&iqp, 0, sizeof(struct csio_iq_params));
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
64362306a36Sopenharmony_ci	if (!mbp)
64462306a36Sopenharmony_ci		return -ENOMEM;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	iqp.pfn		= hw->pfn;
64762306a36Sopenharmony_ci	iqp.vfn		= 0;
64862306a36Sopenharmony_ci	iqp.iqid	= csio_q_iqid(hw, iq_idx);
64962306a36Sopenharmony_ci	iqp.type	= FW_IQ_TYPE_FL_INT_CAP;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	flq_idx = csio_q_iq_flq_idx(hw, iq_idx);
65262306a36Sopenharmony_ci	if (flq_idx != -1)
65362306a36Sopenharmony_ci		iqp.fl0id = csio_q_flid(hw, flq_idx);
65462306a36Sopenharmony_ci	else
65562306a36Sopenharmony_ci		iqp.fl0id = 0xFFFF;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	iqp.fl1id = 0xFFFF;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	csio_mb_iq_free(hw, mbp, priv, CSIO_MB_DEFAULT_TMO, &iqp, cbfn);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	rv = csio_mb_issue(hw, mbp);
66262306a36Sopenharmony_ci	if (rv != 0) {
66362306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
66462306a36Sopenharmony_ci		return rv;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (cbfn != NULL)
66862306a36Sopenharmony_ci		return 0;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	return csio_wr_iq_destroy_rsp(hw, mbp, iq_idx);
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci/*
67462306a36Sopenharmony_ci * csio_wr_eq_destroy_rsp - Response handler for OFLD EQ creation.
67562306a36Sopenharmony_ci * @hw: The HW module.
67662306a36Sopenharmony_ci * @mbp: Mailbox.
67762306a36Sopenharmony_ci * @eq_idx: Egress queue that was freed.
67862306a36Sopenharmony_ci *
67962306a36Sopenharmony_ci * Handle FW_OFLD_EQ_CMD (free) mailbox completion.
68062306a36Sopenharmony_ci */
68162306a36Sopenharmony_cistatic int
68262306a36Sopenharmony_cicsio_wr_eq_destroy_rsp(struct csio_hw *hw, struct csio_mb *mbp, int eq_idx)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	enum fw_retval retval = csio_mb_fw_retval(mbp);
68562306a36Sopenharmony_ci	int rv = 0;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (retval != FW_SUCCESS)
68862306a36Sopenharmony_ci		rv = -EINVAL;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	mempool_free(mbp, hw->mb_mempool);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	return rv;
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci/*
69662306a36Sopenharmony_ci * csio_wr_eq_destroy - Free an Egress queue.
69762306a36Sopenharmony_ci * @hw: The HW module.
69862306a36Sopenharmony_ci * @priv: Private data object.
69962306a36Sopenharmony_ci * @eq_idx: Egress queue index to destroy
70062306a36Sopenharmony_ci * @cbfn: Completion callback.
70162306a36Sopenharmony_ci *
70262306a36Sopenharmony_ci * This API frees an Egress queue by issuing the FW_EQ_OFLD_CMD
70362306a36Sopenharmony_ci * with the free bit set.
70462306a36Sopenharmony_ci */
70562306a36Sopenharmony_cistatic int
70662306a36Sopenharmony_cicsio_wr_eq_destroy(struct csio_hw *hw, void *priv, int eq_idx,
70762306a36Sopenharmony_ci		   void (*cbfn) (struct csio_hw *, struct csio_mb *))
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	int rv = 0;
71062306a36Sopenharmony_ci	struct csio_mb  *mbp;
71162306a36Sopenharmony_ci	struct csio_eq_params eqp;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	memset(&eqp, 0, sizeof(struct csio_eq_params));
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
71662306a36Sopenharmony_ci	if (!mbp)
71762306a36Sopenharmony_ci		return -ENOMEM;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	eqp.pfn		= hw->pfn;
72062306a36Sopenharmony_ci	eqp.vfn		= 0;
72162306a36Sopenharmony_ci	eqp.eqid	= csio_q_eqid(hw, eq_idx);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	csio_mb_eq_ofld_free(hw, mbp, priv, CSIO_MB_DEFAULT_TMO, &eqp, cbfn);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	rv = csio_mb_issue(hw, mbp);
72662306a36Sopenharmony_ci	if (rv != 0) {
72762306a36Sopenharmony_ci		mempool_free(mbp, hw->mb_mempool);
72862306a36Sopenharmony_ci		return rv;
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (cbfn != NULL)
73262306a36Sopenharmony_ci		return 0;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	return csio_wr_eq_destroy_rsp(hw, mbp, eq_idx);
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci/*
73862306a36Sopenharmony_ci * csio_wr_cleanup_eq_stpg - Cleanup Egress queue status page
73962306a36Sopenharmony_ci * @hw: HW module
74062306a36Sopenharmony_ci * @qidx: Egress queue index
74162306a36Sopenharmony_ci *
74262306a36Sopenharmony_ci * Cleanup the Egress queue status page.
74362306a36Sopenharmony_ci */
74462306a36Sopenharmony_cistatic void
74562306a36Sopenharmony_cicsio_wr_cleanup_eq_stpg(struct csio_hw *hw, int qidx)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct csio_q	*q = csio_hw_to_wrm(hw)->q_arr[qidx];
74862306a36Sopenharmony_ci	struct csio_qstatus_page *stp = (struct csio_qstatus_page *)q->vwrap;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	memset(stp, 0, sizeof(*stp));
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci/*
75462306a36Sopenharmony_ci * csio_wr_cleanup_iq_ftr - Cleanup Footer entries in IQ
75562306a36Sopenharmony_ci * @hw: HW module
75662306a36Sopenharmony_ci * @qidx: Ingress queue index
75762306a36Sopenharmony_ci *
75862306a36Sopenharmony_ci * Cleanup the footer entries in the given ingress queue,
75962306a36Sopenharmony_ci * set to 1 the internal copy of genbit.
76062306a36Sopenharmony_ci */
76162306a36Sopenharmony_cistatic void
76262306a36Sopenharmony_cicsio_wr_cleanup_iq_ftr(struct csio_hw *hw, int qidx)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	struct csio_wrm *wrm	= csio_hw_to_wrm(hw);
76562306a36Sopenharmony_ci	struct csio_q	*q	= wrm->q_arr[qidx];
76662306a36Sopenharmony_ci	void *wr;
76762306a36Sopenharmony_ci	struct csio_iqwr_footer *ftr;
76862306a36Sopenharmony_ci	uint32_t i = 0;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	/* set to 1 since we are just about zero out genbit */
77162306a36Sopenharmony_ci	q->un.iq.genbit = 1;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	for (i = 0; i < q->credits; i++) {
77462306a36Sopenharmony_ci		/* Get the WR */
77562306a36Sopenharmony_ci		wr = (void *)((uintptr_t)q->vstart +
77662306a36Sopenharmony_ci					   (i * q->wr_sz));
77762306a36Sopenharmony_ci		/* Get the footer */
77862306a36Sopenharmony_ci		ftr = (struct csio_iqwr_footer *)((uintptr_t)wr +
77962306a36Sopenharmony_ci					  (q->wr_sz - sizeof(*ftr)));
78062306a36Sopenharmony_ci		/* Zero out footer */
78162306a36Sopenharmony_ci		memset(ftr, 0, sizeof(*ftr));
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ciint
78662306a36Sopenharmony_cicsio_wr_destroy_queues(struct csio_hw *hw, bool cmd)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	int i, flq_idx;
78962306a36Sopenharmony_ci	struct csio_q *q;
79062306a36Sopenharmony_ci	struct csio_wrm *wrm = csio_hw_to_wrm(hw);
79162306a36Sopenharmony_ci	int rv;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	for (i = 0; i < wrm->free_qidx; i++) {
79462306a36Sopenharmony_ci		q = wrm->q_arr[i];
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci		switch (q->type) {
79762306a36Sopenharmony_ci		case CSIO_EGRESS:
79862306a36Sopenharmony_ci			if (csio_q_eqid(hw, i) != CSIO_MAX_QID) {
79962306a36Sopenharmony_ci				csio_wr_cleanup_eq_stpg(hw, i);
80062306a36Sopenharmony_ci				if (!cmd) {
80162306a36Sopenharmony_ci					csio_q_eqid(hw, i) = CSIO_MAX_QID;
80262306a36Sopenharmony_ci					continue;
80362306a36Sopenharmony_ci				}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci				rv = csio_wr_eq_destroy(hw, NULL, i, NULL);
80662306a36Sopenharmony_ci				if ((rv == -EBUSY) || (rv == -ETIMEDOUT))
80762306a36Sopenharmony_ci					cmd = false;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci				csio_q_eqid(hw, i) = CSIO_MAX_QID;
81062306a36Sopenharmony_ci			}
81162306a36Sopenharmony_ci			fallthrough;
81262306a36Sopenharmony_ci		case CSIO_INGRESS:
81362306a36Sopenharmony_ci			if (csio_q_iqid(hw, i) != CSIO_MAX_QID) {
81462306a36Sopenharmony_ci				csio_wr_cleanup_iq_ftr(hw, i);
81562306a36Sopenharmony_ci				if (!cmd) {
81662306a36Sopenharmony_ci					csio_q_iqid(hw, i) = CSIO_MAX_QID;
81762306a36Sopenharmony_ci					flq_idx = csio_q_iq_flq_idx(hw, i);
81862306a36Sopenharmony_ci					if (flq_idx != -1)
81962306a36Sopenharmony_ci						csio_q_flid(hw, flq_idx) =
82062306a36Sopenharmony_ci								CSIO_MAX_QID;
82162306a36Sopenharmony_ci					continue;
82262306a36Sopenharmony_ci				}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci				rv = csio_wr_iq_destroy(hw, NULL, i, NULL);
82562306a36Sopenharmony_ci				if ((rv == -EBUSY) || (rv == -ETIMEDOUT))
82662306a36Sopenharmony_ci					cmd = false;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci				csio_q_iqid(hw, i) = CSIO_MAX_QID;
82962306a36Sopenharmony_ci				flq_idx = csio_q_iq_flq_idx(hw, i);
83062306a36Sopenharmony_ci				if (flq_idx != -1)
83162306a36Sopenharmony_ci					csio_q_flid(hw, flq_idx) = CSIO_MAX_QID;
83262306a36Sopenharmony_ci			}
83362306a36Sopenharmony_ci			break;
83462306a36Sopenharmony_ci		default:
83562306a36Sopenharmony_ci			break;
83662306a36Sopenharmony_ci		}
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	hw->flags &= ~CSIO_HWF_Q_FW_ALLOCED;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	return 0;
84262306a36Sopenharmony_ci}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci/*
84562306a36Sopenharmony_ci * csio_wr_get - Get requested size of WR entry/entries from queue.
84662306a36Sopenharmony_ci * @hw: HW module.
84762306a36Sopenharmony_ci * @qidx: Index of queue.
84862306a36Sopenharmony_ci * @size: Cumulative size of Work request(s).
84962306a36Sopenharmony_ci * @wrp: Work request pair.
85062306a36Sopenharmony_ci *
85162306a36Sopenharmony_ci * If requested credits are available, return the start address of the
85262306a36Sopenharmony_ci * work request in the work request pair. Set pidx accordingly and
85362306a36Sopenharmony_ci * return.
85462306a36Sopenharmony_ci *
85562306a36Sopenharmony_ci * NOTE about WR pair:
85662306a36Sopenharmony_ci * ==================
85762306a36Sopenharmony_ci * A WR can start towards the end of a queue, and then continue at the
85862306a36Sopenharmony_ci * beginning, since the queue is considered to be circular. This will
85962306a36Sopenharmony_ci * require a pair of address/size to be passed back to the caller -
86062306a36Sopenharmony_ci * hence Work request pair format.
86162306a36Sopenharmony_ci */
86262306a36Sopenharmony_ciint
86362306a36Sopenharmony_cicsio_wr_get(struct csio_hw *hw, int qidx, uint32_t size,
86462306a36Sopenharmony_ci	    struct csio_wr_pair *wrp)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	struct csio_wrm *wrm = csio_hw_to_wrm(hw);
86762306a36Sopenharmony_ci	struct csio_q *q = wrm->q_arr[qidx];
86862306a36Sopenharmony_ci	void *cwr = (void *)((uintptr_t)(q->vstart) +
86962306a36Sopenharmony_ci						(q->pidx * CSIO_QCREDIT_SZ));
87062306a36Sopenharmony_ci	struct csio_qstatus_page *stp = (struct csio_qstatus_page *)q->vwrap;
87162306a36Sopenharmony_ci	uint16_t cidx = q->cidx = ntohs(stp->cidx);
87262306a36Sopenharmony_ci	uint16_t pidx = q->pidx;
87362306a36Sopenharmony_ci	uint32_t req_sz	= ALIGN(size, CSIO_QCREDIT_SZ);
87462306a36Sopenharmony_ci	int req_credits	= req_sz / CSIO_QCREDIT_SZ;
87562306a36Sopenharmony_ci	int credits;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	CSIO_DB_ASSERT(q->owner != NULL);
87862306a36Sopenharmony_ci	CSIO_DB_ASSERT((qidx >= 0) && (qidx < wrm->free_qidx));
87962306a36Sopenharmony_ci	CSIO_DB_ASSERT(cidx <= q->credits);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* Calculate credits */
88262306a36Sopenharmony_ci	if (pidx > cidx) {
88362306a36Sopenharmony_ci		credits = q->credits - (pidx - cidx) - 1;
88462306a36Sopenharmony_ci	} else if (cidx > pidx) {
88562306a36Sopenharmony_ci		credits = cidx - pidx - 1;
88662306a36Sopenharmony_ci	} else {
88762306a36Sopenharmony_ci		/* cidx == pidx, empty queue */
88862306a36Sopenharmony_ci		credits = q->credits;
88962306a36Sopenharmony_ci		CSIO_INC_STATS(q, n_qempty);
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	/*
89362306a36Sopenharmony_ci	 * Check if we have enough credits.
89462306a36Sopenharmony_ci	 * credits = 1 implies queue is full.
89562306a36Sopenharmony_ci	 */
89662306a36Sopenharmony_ci	if (!credits || (req_credits > credits)) {
89762306a36Sopenharmony_ci		CSIO_INC_STATS(q, n_qfull);
89862306a36Sopenharmony_ci		return -EBUSY;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	/*
90262306a36Sopenharmony_ci	 * If we are here, we have enough credits to satisfy the
90362306a36Sopenharmony_ci	 * request. Check if we are near the end of q, and if WR spills over.
90462306a36Sopenharmony_ci	 * If it does, use the first addr/size to cover the queue until
90562306a36Sopenharmony_ci	 * the end. Fit the remainder portion of the request at the top
90662306a36Sopenharmony_ci	 * of queue and return it in the second addr/len. Set pidx
90762306a36Sopenharmony_ci	 * accordingly.
90862306a36Sopenharmony_ci	 */
90962306a36Sopenharmony_ci	if (unlikely(((uintptr_t)cwr + req_sz) > (uintptr_t)(q->vwrap))) {
91062306a36Sopenharmony_ci		wrp->addr1 = cwr;
91162306a36Sopenharmony_ci		wrp->size1 = (uint32_t)((uintptr_t)q->vwrap - (uintptr_t)cwr);
91262306a36Sopenharmony_ci		wrp->addr2 = q->vstart;
91362306a36Sopenharmony_ci		wrp->size2 = req_sz - wrp->size1;
91462306a36Sopenharmony_ci		q->pidx	= (uint16_t)(ALIGN(wrp->size2, CSIO_QCREDIT_SZ) /
91562306a36Sopenharmony_ci							CSIO_QCREDIT_SZ);
91662306a36Sopenharmony_ci		CSIO_INC_STATS(q, n_qwrap);
91762306a36Sopenharmony_ci		CSIO_INC_STATS(q, n_eq_wr_split);
91862306a36Sopenharmony_ci	} else {
91962306a36Sopenharmony_ci		wrp->addr1 = cwr;
92062306a36Sopenharmony_ci		wrp->size1 = req_sz;
92162306a36Sopenharmony_ci		wrp->addr2 = NULL;
92262306a36Sopenharmony_ci		wrp->size2 = 0;
92362306a36Sopenharmony_ci		q->pidx	+= (uint16_t)req_credits;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci		/* We are the end of queue, roll back pidx to top of queue */
92662306a36Sopenharmony_ci		if (unlikely(q->pidx == q->credits)) {
92762306a36Sopenharmony_ci			q->pidx = 0;
92862306a36Sopenharmony_ci			CSIO_INC_STATS(q, n_qwrap);
92962306a36Sopenharmony_ci		}
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	q->inc_idx = (uint16_t)req_credits;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	CSIO_INC_STATS(q, n_tot_reqs);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	return 0;
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci/*
94062306a36Sopenharmony_ci * csio_wr_copy_to_wrp - Copies given data into WR.
94162306a36Sopenharmony_ci * @data_buf - Data buffer
94262306a36Sopenharmony_ci * @wrp - Work request pair.
94362306a36Sopenharmony_ci * @wr_off - Work request offset.
94462306a36Sopenharmony_ci * @data_len - Data length.
94562306a36Sopenharmony_ci *
94662306a36Sopenharmony_ci * Copies the given data in Work Request. Work request pair(wrp) specifies
94762306a36Sopenharmony_ci * address information of Work request.
94862306a36Sopenharmony_ci * Returns: none
94962306a36Sopenharmony_ci */
95062306a36Sopenharmony_civoid
95162306a36Sopenharmony_cicsio_wr_copy_to_wrp(void *data_buf, struct csio_wr_pair *wrp,
95262306a36Sopenharmony_ci		   uint32_t wr_off, uint32_t data_len)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	uint32_t nbytes;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	/* Number of space available in buffer addr1 of WRP */
95762306a36Sopenharmony_ci	nbytes = ((wrp->size1 - wr_off) >= data_len) ?
95862306a36Sopenharmony_ci					data_len : (wrp->size1 - wr_off);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	memcpy((uint8_t *) wrp->addr1 + wr_off, data_buf, nbytes);
96162306a36Sopenharmony_ci	data_len -= nbytes;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	/* Write the remaining data from the begining of circular buffer */
96462306a36Sopenharmony_ci	if (data_len) {
96562306a36Sopenharmony_ci		CSIO_DB_ASSERT(data_len <= wrp->size2);
96662306a36Sopenharmony_ci		CSIO_DB_ASSERT(wrp->addr2 != NULL);
96762306a36Sopenharmony_ci		memcpy(wrp->addr2, (uint8_t *) data_buf + nbytes, data_len);
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci/*
97262306a36Sopenharmony_ci * csio_wr_issue - Notify chip of Work request.
97362306a36Sopenharmony_ci * @hw: HW module.
97462306a36Sopenharmony_ci * @qidx: Index of queue.
97562306a36Sopenharmony_ci * @prio: 0: Low priority, 1: High priority
97662306a36Sopenharmony_ci *
97762306a36Sopenharmony_ci * Rings the SGE Doorbell by writing the current producer index of the passed
97862306a36Sopenharmony_ci * in queue into the register.
97962306a36Sopenharmony_ci *
98062306a36Sopenharmony_ci */
98162306a36Sopenharmony_ciint
98262306a36Sopenharmony_cicsio_wr_issue(struct csio_hw *hw, int qidx, bool prio)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	struct csio_wrm *wrm = csio_hw_to_wrm(hw);
98562306a36Sopenharmony_ci	struct csio_q *q = wrm->q_arr[qidx];
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	CSIO_DB_ASSERT((qidx >= 0) && (qidx < wrm->free_qidx));
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	wmb();
99062306a36Sopenharmony_ci	/* Ring SGE Doorbell writing q->pidx into it */
99162306a36Sopenharmony_ci	csio_wr_reg32(hw, DBPRIO_V(prio) | QID_V(q->un.eq.physeqid) |
99262306a36Sopenharmony_ci			  PIDX_T5_V(q->inc_idx) | DBTYPE_F,
99362306a36Sopenharmony_ci			  MYPF_REG(SGE_PF_KDOORBELL_A));
99462306a36Sopenharmony_ci	q->inc_idx = 0;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	return 0;
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cistatic inline uint32_t
100062306a36Sopenharmony_cicsio_wr_avail_qcredits(struct csio_q *q)
100162306a36Sopenharmony_ci{
100262306a36Sopenharmony_ci	if (q->pidx > q->cidx)
100362306a36Sopenharmony_ci		return q->pidx - q->cidx;
100462306a36Sopenharmony_ci	else if (q->cidx > q->pidx)
100562306a36Sopenharmony_ci		return q->credits - (q->cidx - q->pidx);
100662306a36Sopenharmony_ci	else
100762306a36Sopenharmony_ci		return 0;	/* cidx == pidx, empty queue */
100862306a36Sopenharmony_ci}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci/*
101162306a36Sopenharmony_ci * csio_wr_inval_flq_buf - Invalidate a free list buffer entry.
101262306a36Sopenharmony_ci * @hw: HW module.
101362306a36Sopenharmony_ci * @flq: The freelist queue.
101462306a36Sopenharmony_ci *
101562306a36Sopenharmony_ci * Invalidate the driver's version of a freelist buffer entry,
101662306a36Sopenharmony_ci * without freeing the associated the DMA memory. The entry
101762306a36Sopenharmony_ci * to be invalidated is picked up from the current Free list
101862306a36Sopenharmony_ci * queue cidx.
101962306a36Sopenharmony_ci *
102062306a36Sopenharmony_ci */
102162306a36Sopenharmony_cistatic inline void
102262306a36Sopenharmony_cicsio_wr_inval_flq_buf(struct csio_hw *hw, struct csio_q *flq)
102362306a36Sopenharmony_ci{
102462306a36Sopenharmony_ci	flq->cidx++;
102562306a36Sopenharmony_ci	if (flq->cidx == flq->credits) {
102662306a36Sopenharmony_ci		flq->cidx = 0;
102762306a36Sopenharmony_ci		CSIO_INC_STATS(flq, n_qwrap);
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci/*
103262306a36Sopenharmony_ci * csio_wr_process_fl - Process a freelist completion.
103362306a36Sopenharmony_ci * @hw: HW module.
103462306a36Sopenharmony_ci * @q: The ingress queue attached to the Freelist.
103562306a36Sopenharmony_ci * @wr: The freelist completion WR in the ingress queue.
103662306a36Sopenharmony_ci * @len_to_qid: The lower 32-bits of the first flit of the RSP footer
103762306a36Sopenharmony_ci * @iq_handler: Caller's handler for this completion.
103862306a36Sopenharmony_ci * @priv: Private pointer of caller
103962306a36Sopenharmony_ci *
104062306a36Sopenharmony_ci */
104162306a36Sopenharmony_cistatic inline void
104262306a36Sopenharmony_cicsio_wr_process_fl(struct csio_hw *hw, struct csio_q *q,
104362306a36Sopenharmony_ci		   void *wr, uint32_t len_to_qid,
104462306a36Sopenharmony_ci		   void (*iq_handler)(struct csio_hw *, void *,
104562306a36Sopenharmony_ci				      uint32_t, struct csio_fl_dma_buf *,
104662306a36Sopenharmony_ci				      void *),
104762306a36Sopenharmony_ci		   void *priv)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	struct csio_wrm *wrm = csio_hw_to_wrm(hw);
105062306a36Sopenharmony_ci	struct csio_sge *sge = &wrm->sge;
105162306a36Sopenharmony_ci	struct csio_fl_dma_buf flb;
105262306a36Sopenharmony_ci	struct csio_dma_buf *buf, *fbuf;
105362306a36Sopenharmony_ci	uint32_t bufsz, len, lastlen = 0;
105462306a36Sopenharmony_ci	struct csio_q *flq = hw->wrm.q_arr[q->un.iq.flq_idx];
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	CSIO_DB_ASSERT(flq != NULL);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	len = len_to_qid;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (len & IQWRF_NEWBUF) {
106162306a36Sopenharmony_ci		if (flq->un.fl.offset > 0) {
106262306a36Sopenharmony_ci			csio_wr_inval_flq_buf(hw, flq);
106362306a36Sopenharmony_ci			flq->un.fl.offset = 0;
106462306a36Sopenharmony_ci		}
106562306a36Sopenharmony_ci		len = IQWRF_LEN_GET(len);
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	CSIO_DB_ASSERT(len != 0);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	flb.totlen = len;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	/* Consume all freelist buffers used for len bytes */
107362306a36Sopenharmony_ci	for (fbuf = flb.flbufs; ; fbuf++) {
107462306a36Sopenharmony_ci		buf = &flq->un.fl.bufs[flq->cidx];
107562306a36Sopenharmony_ci		bufsz = csio_wr_fl_bufsz(sge, buf);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci		fbuf->paddr	= buf->paddr;
107862306a36Sopenharmony_ci		fbuf->vaddr	= buf->vaddr;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci		flb.offset	= flq->un.fl.offset;
108162306a36Sopenharmony_ci		lastlen		= min(bufsz, len);
108262306a36Sopenharmony_ci		fbuf->len	= lastlen;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci		len -= lastlen;
108562306a36Sopenharmony_ci		if (!len)
108662306a36Sopenharmony_ci			break;
108762306a36Sopenharmony_ci		csio_wr_inval_flq_buf(hw, flq);
108862306a36Sopenharmony_ci	}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	flb.defer_free = flq->un.fl.packen ? 0 : 1;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	iq_handler(hw, wr, q->wr_sz - sizeof(struct csio_iqwr_footer),
109362306a36Sopenharmony_ci		   &flb, priv);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	if (flq->un.fl.packen)
109662306a36Sopenharmony_ci		flq->un.fl.offset += ALIGN(lastlen, sge->csio_fl_align);
109762306a36Sopenharmony_ci	else
109862306a36Sopenharmony_ci		csio_wr_inval_flq_buf(hw, flq);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci/*
110362306a36Sopenharmony_ci * csio_is_new_iqwr - Is this a new Ingress queue entry ?
110462306a36Sopenharmony_ci * @q: Ingress quueue.
110562306a36Sopenharmony_ci * @ftr: Ingress queue WR SGE footer.
110662306a36Sopenharmony_ci *
110762306a36Sopenharmony_ci * The entry is new if our generation bit matches the corresponding
110862306a36Sopenharmony_ci * bit in the footer of the current WR.
110962306a36Sopenharmony_ci */
111062306a36Sopenharmony_cistatic inline bool
111162306a36Sopenharmony_cicsio_is_new_iqwr(struct csio_q *q, struct csio_iqwr_footer *ftr)
111262306a36Sopenharmony_ci{
111362306a36Sopenharmony_ci	return (q->un.iq.genbit == (ftr->u.type_gen >> IQWRF_GEN_SHIFT));
111462306a36Sopenharmony_ci}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci/*
111762306a36Sopenharmony_ci * csio_wr_process_iq - Process elements in Ingress queue.
111862306a36Sopenharmony_ci * @hw:  HW pointer
111962306a36Sopenharmony_ci * @qidx: Index of queue
112062306a36Sopenharmony_ci * @iq_handler: Handler for this queue
112162306a36Sopenharmony_ci * @priv: Caller's private pointer
112262306a36Sopenharmony_ci *
112362306a36Sopenharmony_ci * This routine walks through every entry of the ingress queue, calling
112462306a36Sopenharmony_ci * the provided iq_handler with the entry, until the generation bit
112562306a36Sopenharmony_ci * flips.
112662306a36Sopenharmony_ci */
112762306a36Sopenharmony_ciint
112862306a36Sopenharmony_cicsio_wr_process_iq(struct csio_hw *hw, struct csio_q *q,
112962306a36Sopenharmony_ci		   void (*iq_handler)(struct csio_hw *, void *,
113062306a36Sopenharmony_ci				      uint32_t, struct csio_fl_dma_buf *,
113162306a36Sopenharmony_ci				      void *),
113262306a36Sopenharmony_ci		   void *priv)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	struct csio_wrm *wrm = csio_hw_to_wrm(hw);
113562306a36Sopenharmony_ci	void *wr = (void *)((uintptr_t)q->vstart + (q->cidx * q->wr_sz));
113662306a36Sopenharmony_ci	struct csio_iqwr_footer *ftr;
113762306a36Sopenharmony_ci	uint32_t wr_type, fw_qid, qid;
113862306a36Sopenharmony_ci	struct csio_q *q_completed;
113962306a36Sopenharmony_ci	struct csio_q *flq = csio_iq_has_fl(q) ?
114062306a36Sopenharmony_ci					wrm->q_arr[q->un.iq.flq_idx] : NULL;
114162306a36Sopenharmony_ci	int rv = 0;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	/* Get the footer */
114462306a36Sopenharmony_ci	ftr = (struct csio_iqwr_footer *)((uintptr_t)wr +
114562306a36Sopenharmony_ci					  (q->wr_sz - sizeof(*ftr)));
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	/*
114862306a36Sopenharmony_ci	 * When q wrapped around last time, driver should have inverted
114962306a36Sopenharmony_ci	 * ic.genbit as well.
115062306a36Sopenharmony_ci	 */
115162306a36Sopenharmony_ci	while (csio_is_new_iqwr(q, ftr)) {
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci		CSIO_DB_ASSERT(((uintptr_t)wr + q->wr_sz) <=
115462306a36Sopenharmony_ci						(uintptr_t)q->vwrap);
115562306a36Sopenharmony_ci		rmb();
115662306a36Sopenharmony_ci		wr_type = IQWRF_TYPE_GET(ftr->u.type_gen);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci		switch (wr_type) {
115962306a36Sopenharmony_ci		case X_RSPD_TYPE_CPL:
116062306a36Sopenharmony_ci			/* Subtract footer from WR len */
116162306a36Sopenharmony_ci			iq_handler(hw, wr, q->wr_sz - sizeof(*ftr), NULL, priv);
116262306a36Sopenharmony_ci			break;
116362306a36Sopenharmony_ci		case X_RSPD_TYPE_FLBUF:
116462306a36Sopenharmony_ci			csio_wr_process_fl(hw, q, wr,
116562306a36Sopenharmony_ci					   ntohl(ftr->pldbuflen_qid),
116662306a36Sopenharmony_ci					   iq_handler, priv);
116762306a36Sopenharmony_ci			break;
116862306a36Sopenharmony_ci		case X_RSPD_TYPE_INTR:
116962306a36Sopenharmony_ci			fw_qid = ntohl(ftr->pldbuflen_qid);
117062306a36Sopenharmony_ci			qid = fw_qid - wrm->fw_iq_start;
117162306a36Sopenharmony_ci			q_completed = hw->wrm.intr_map[qid];
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci			if (unlikely(qid ==
117462306a36Sopenharmony_ci					csio_q_physiqid(hw, hw->intr_iq_idx))) {
117562306a36Sopenharmony_ci				/*
117662306a36Sopenharmony_ci				 * We are already in the Forward Interrupt
117762306a36Sopenharmony_ci				 * Interrupt Queue Service! Do-not service
117862306a36Sopenharmony_ci				 * again!
117962306a36Sopenharmony_ci				 *
118062306a36Sopenharmony_ci				 */
118162306a36Sopenharmony_ci			} else {
118262306a36Sopenharmony_ci				CSIO_DB_ASSERT(q_completed);
118362306a36Sopenharmony_ci				CSIO_DB_ASSERT(
118462306a36Sopenharmony_ci					q_completed->un.iq.iq_intx_handler);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci				/* Call the queue handler. */
118762306a36Sopenharmony_ci				q_completed->un.iq.iq_intx_handler(hw, NULL,
118862306a36Sopenharmony_ci						0, NULL, (void *)q_completed);
118962306a36Sopenharmony_ci			}
119062306a36Sopenharmony_ci			break;
119162306a36Sopenharmony_ci		default:
119262306a36Sopenharmony_ci			csio_warn(hw, "Unknown resp type 0x%x received\n",
119362306a36Sopenharmony_ci				 wr_type);
119462306a36Sopenharmony_ci			CSIO_INC_STATS(q, n_rsp_unknown);
119562306a36Sopenharmony_ci			break;
119662306a36Sopenharmony_ci		}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci		/*
119962306a36Sopenharmony_ci		 * Ingress *always* has fixed size WR entries. Therefore,
120062306a36Sopenharmony_ci		 * there should always be complete WRs towards the end of
120162306a36Sopenharmony_ci		 * queue.
120262306a36Sopenharmony_ci		 */
120362306a36Sopenharmony_ci		if (((uintptr_t)wr + q->wr_sz) == (uintptr_t)q->vwrap) {
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci			/* Roll over to start of queue */
120662306a36Sopenharmony_ci			q->cidx = 0;
120762306a36Sopenharmony_ci			wr	= q->vstart;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci			/* Toggle genbit */
121062306a36Sopenharmony_ci			q->un.iq.genbit ^= 0x1;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci			CSIO_INC_STATS(q, n_qwrap);
121362306a36Sopenharmony_ci		} else {
121462306a36Sopenharmony_ci			q->cidx++;
121562306a36Sopenharmony_ci			wr	= (void *)((uintptr_t)(q->vstart) +
121662306a36Sopenharmony_ci					   (q->cidx * q->wr_sz));
121762306a36Sopenharmony_ci		}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci		ftr = (struct csio_iqwr_footer *)((uintptr_t)wr +
122062306a36Sopenharmony_ci						  (q->wr_sz - sizeof(*ftr)));
122162306a36Sopenharmony_ci		q->inc_idx++;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	} /* while (q->un.iq.genbit == hdr->genbit) */
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	/*
122662306a36Sopenharmony_ci	 * We need to re-arm SGE interrupts in case we got a stray interrupt,
122762306a36Sopenharmony_ci	 * especially in msix mode. With INTx, this may be a common occurence.
122862306a36Sopenharmony_ci	 */
122962306a36Sopenharmony_ci	if (unlikely(!q->inc_idx)) {
123062306a36Sopenharmony_ci		CSIO_INC_STATS(q, n_stray_comp);
123162306a36Sopenharmony_ci		rv = -EINVAL;
123262306a36Sopenharmony_ci		goto restart;
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	/* Replenish free list buffers if pending falls below low water mark */
123662306a36Sopenharmony_ci	if (flq) {
123762306a36Sopenharmony_ci		uint32_t avail  = csio_wr_avail_qcredits(flq);
123862306a36Sopenharmony_ci		if (avail <= 16) {
123962306a36Sopenharmony_ci			/* Make sure in FLQ, atleast 1 credit (8 FL buffers)
124062306a36Sopenharmony_ci			 * remains unpopulated otherwise HW thinks
124162306a36Sopenharmony_ci			 * FLQ is empty.
124262306a36Sopenharmony_ci			 */
124362306a36Sopenharmony_ci			csio_wr_update_fl(hw, flq, (flq->credits - 8) - avail);
124462306a36Sopenharmony_ci			csio_wr_ring_fldb(hw, flq);
124562306a36Sopenharmony_ci		}
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cirestart:
124962306a36Sopenharmony_ci	/* Now inform SGE about our incremental index value */
125062306a36Sopenharmony_ci	csio_wr_reg32(hw, CIDXINC_V(q->inc_idx)		|
125162306a36Sopenharmony_ci			  INGRESSQID_V(q->un.iq.physiqid)	|
125262306a36Sopenharmony_ci			  TIMERREG_V(csio_sge_timer_reg),
125362306a36Sopenharmony_ci			  MYPF_REG(SGE_PF_GTS_A));
125462306a36Sopenharmony_ci	q->stats.n_tot_rsps += q->inc_idx;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	q->inc_idx = 0;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	return rv;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ciint
126262306a36Sopenharmony_cicsio_wr_process_iq_idx(struct csio_hw *hw, int qidx,
126362306a36Sopenharmony_ci		   void (*iq_handler)(struct csio_hw *, void *,
126462306a36Sopenharmony_ci				      uint32_t, struct csio_fl_dma_buf *,
126562306a36Sopenharmony_ci				      void *),
126662306a36Sopenharmony_ci		   void *priv)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	struct csio_wrm *wrm	= csio_hw_to_wrm(hw);
126962306a36Sopenharmony_ci	struct csio_q	*iq	= wrm->q_arr[qidx];
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	return csio_wr_process_iq(hw, iq, iq_handler, priv);
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic int
127562306a36Sopenharmony_cicsio_closest_timer(struct csio_sge *s, int time)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	int i, delta, match = 0, min_delta = INT_MAX;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(s->timer_val); i++) {
128062306a36Sopenharmony_ci		delta = time - s->timer_val[i];
128162306a36Sopenharmony_ci		if (delta < 0)
128262306a36Sopenharmony_ci			delta = -delta;
128362306a36Sopenharmony_ci		if (delta < min_delta) {
128462306a36Sopenharmony_ci			min_delta = delta;
128562306a36Sopenharmony_ci			match = i;
128662306a36Sopenharmony_ci		}
128762306a36Sopenharmony_ci	}
128862306a36Sopenharmony_ci	return match;
128962306a36Sopenharmony_ci}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_cistatic int
129262306a36Sopenharmony_cicsio_closest_thresh(struct csio_sge *s, int cnt)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	int i, delta, match = 0, min_delta = INT_MAX;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(s->counter_val); i++) {
129762306a36Sopenharmony_ci		delta = cnt - s->counter_val[i];
129862306a36Sopenharmony_ci		if (delta < 0)
129962306a36Sopenharmony_ci			delta = -delta;
130062306a36Sopenharmony_ci		if (delta < min_delta) {
130162306a36Sopenharmony_ci			min_delta = delta;
130262306a36Sopenharmony_ci			match = i;
130362306a36Sopenharmony_ci		}
130462306a36Sopenharmony_ci	}
130562306a36Sopenharmony_ci	return match;
130662306a36Sopenharmony_ci}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_cistatic void
130962306a36Sopenharmony_cicsio_wr_fixup_host_params(struct csio_hw *hw)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	struct csio_wrm *wrm = csio_hw_to_wrm(hw);
131262306a36Sopenharmony_ci	struct csio_sge *sge = &wrm->sge;
131362306a36Sopenharmony_ci	uint32_t clsz = L1_CACHE_BYTES;
131462306a36Sopenharmony_ci	uint32_t s_hps = PAGE_SHIFT - 10;
131562306a36Sopenharmony_ci	uint32_t stat_len = clsz > 64 ? 128 : 64;
131662306a36Sopenharmony_ci	u32 fl_align = clsz < 32 ? 32 : clsz;
131762306a36Sopenharmony_ci	u32 pack_align;
131862306a36Sopenharmony_ci	u32 ingpad, ingpack;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	csio_wr_reg32(hw, HOSTPAGESIZEPF0_V(s_hps) | HOSTPAGESIZEPF1_V(s_hps) |
132162306a36Sopenharmony_ci		      HOSTPAGESIZEPF2_V(s_hps) | HOSTPAGESIZEPF3_V(s_hps) |
132262306a36Sopenharmony_ci		      HOSTPAGESIZEPF4_V(s_hps) | HOSTPAGESIZEPF5_V(s_hps) |
132362306a36Sopenharmony_ci		      HOSTPAGESIZEPF6_V(s_hps) | HOSTPAGESIZEPF7_V(s_hps),
132462306a36Sopenharmony_ci		      SGE_HOST_PAGE_SIZE_A);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	/* T5 introduced the separation of the Free List Padding and
132762306a36Sopenharmony_ci	 * Packing Boundaries.  Thus, we can select a smaller Padding
132862306a36Sopenharmony_ci	 * Boundary to avoid uselessly chewing up PCIe Link and Memory
132962306a36Sopenharmony_ci	 * Bandwidth, and use a Packing Boundary which is large enough
133062306a36Sopenharmony_ci	 * to avoid false sharing between CPUs, etc.
133162306a36Sopenharmony_ci	 *
133262306a36Sopenharmony_ci	 * For the PCI Link, the smaller the Padding Boundary the
133362306a36Sopenharmony_ci	 * better.  For the Memory Controller, a smaller Padding
133462306a36Sopenharmony_ci	 * Boundary is better until we cross under the Memory Line
133562306a36Sopenharmony_ci	 * Size (the minimum unit of transfer to/from Memory).  If we
133662306a36Sopenharmony_ci	 * have a Padding Boundary which is smaller than the Memory
133762306a36Sopenharmony_ci	 * Line Size, that'll involve a Read-Modify-Write cycle on the
133862306a36Sopenharmony_ci	 * Memory Controller which is never good.
133962306a36Sopenharmony_ci	 */
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	/* We want the Packing Boundary to be based on the Cache Line
134262306a36Sopenharmony_ci	 * Size in order to help avoid False Sharing performance
134362306a36Sopenharmony_ci	 * issues between CPUs, etc.  We also want the Packing
134462306a36Sopenharmony_ci	 * Boundary to incorporate the PCI-E Maximum Payload Size.  We
134562306a36Sopenharmony_ci	 * get best performance when the Packing Boundary is a
134662306a36Sopenharmony_ci	 * multiple of the Maximum Payload Size.
134762306a36Sopenharmony_ci	 */
134862306a36Sopenharmony_ci	pack_align = fl_align;
134962306a36Sopenharmony_ci	if (pci_is_pcie(hw->pdev)) {
135062306a36Sopenharmony_ci		u32 mps, mps_log;
135162306a36Sopenharmony_ci		u16 devctl;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci		/* The PCIe Device Control Maximum Payload Size field
135462306a36Sopenharmony_ci		 * [bits 7:5] encodes sizes as powers of 2 starting at
135562306a36Sopenharmony_ci		 * 128 bytes.
135662306a36Sopenharmony_ci		 */
135762306a36Sopenharmony_ci		pcie_capability_read_word(hw->pdev, PCI_EXP_DEVCTL, &devctl);
135862306a36Sopenharmony_ci		mps_log = ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5) + 7;
135962306a36Sopenharmony_ci		mps = 1 << mps_log;
136062306a36Sopenharmony_ci		if (mps > pack_align)
136162306a36Sopenharmony_ci			pack_align = mps;
136262306a36Sopenharmony_ci	}
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	/* T5/T6 have a special interpretation of the "0"
136562306a36Sopenharmony_ci	 * value for the Packing Boundary.  This corresponds to 16
136662306a36Sopenharmony_ci	 * bytes instead of the expected 32 bytes.
136762306a36Sopenharmony_ci	 */
136862306a36Sopenharmony_ci	if (pack_align <= 16) {
136962306a36Sopenharmony_ci		ingpack = INGPACKBOUNDARY_16B_X;
137062306a36Sopenharmony_ci		fl_align = 16;
137162306a36Sopenharmony_ci	} else if (pack_align == 32) {
137262306a36Sopenharmony_ci		ingpack = INGPACKBOUNDARY_64B_X;
137362306a36Sopenharmony_ci		fl_align = 64;
137462306a36Sopenharmony_ci	} else {
137562306a36Sopenharmony_ci		u32 pack_align_log = fls(pack_align) - 1;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci		ingpack = pack_align_log - INGPACKBOUNDARY_SHIFT_X;
137862306a36Sopenharmony_ci		fl_align = pack_align;
137962306a36Sopenharmony_ci	}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	/* Use the smallest Ingress Padding which isn't smaller than
138262306a36Sopenharmony_ci	 * the Memory Controller Read/Write Size.  We'll take that as
138362306a36Sopenharmony_ci	 * being 8 bytes since we don't know of any system with a
138462306a36Sopenharmony_ci	 * wider Memory Controller Bus Width.
138562306a36Sopenharmony_ci	 */
138662306a36Sopenharmony_ci	if (csio_is_t5(hw->pdev->device & CSIO_HW_CHIP_MASK))
138762306a36Sopenharmony_ci		ingpad = INGPADBOUNDARY_32B_X;
138862306a36Sopenharmony_ci	else
138962306a36Sopenharmony_ci		ingpad = T6_INGPADBOUNDARY_8B_X;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	csio_set_reg_field(hw, SGE_CONTROL_A,
139262306a36Sopenharmony_ci			   INGPADBOUNDARY_V(INGPADBOUNDARY_M) |
139362306a36Sopenharmony_ci			   EGRSTATUSPAGESIZE_F,
139462306a36Sopenharmony_ci			   INGPADBOUNDARY_V(ingpad) |
139562306a36Sopenharmony_ci			   EGRSTATUSPAGESIZE_V(stat_len != 64));
139662306a36Sopenharmony_ci	csio_set_reg_field(hw, SGE_CONTROL2_A,
139762306a36Sopenharmony_ci			   INGPACKBOUNDARY_V(INGPACKBOUNDARY_M),
139862306a36Sopenharmony_ci			   INGPACKBOUNDARY_V(ingpack));
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	/* FL BUFFER SIZE#0 is Page size i,e already aligned to cache line */
140162306a36Sopenharmony_ci	csio_wr_reg32(hw, PAGE_SIZE, SGE_FL_BUFFER_SIZE0_A);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	/*
140462306a36Sopenharmony_ci	 * If using hard params, the following will get set correctly
140562306a36Sopenharmony_ci	 * in csio_wr_set_sge().
140662306a36Sopenharmony_ci	 */
140762306a36Sopenharmony_ci	if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS) {
140862306a36Sopenharmony_ci		csio_wr_reg32(hw,
140962306a36Sopenharmony_ci			(csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE2_A) +
141062306a36Sopenharmony_ci			fl_align - 1) & ~(fl_align - 1),
141162306a36Sopenharmony_ci			SGE_FL_BUFFER_SIZE2_A);
141262306a36Sopenharmony_ci		csio_wr_reg32(hw,
141362306a36Sopenharmony_ci			(csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE3_A) +
141462306a36Sopenharmony_ci			fl_align - 1) & ~(fl_align - 1),
141562306a36Sopenharmony_ci			SGE_FL_BUFFER_SIZE3_A);
141662306a36Sopenharmony_ci	}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	sge->csio_fl_align = fl_align;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	csio_wr_reg32(hw, HPZ0_V(PAGE_SHIFT - 12), ULP_RX_TDDP_PSZ_A);
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	/* default value of rx_dma_offset of the NIC driver */
142362306a36Sopenharmony_ci	csio_set_reg_field(hw, SGE_CONTROL_A,
142462306a36Sopenharmony_ci			   PKTSHIFT_V(PKTSHIFT_M),
142562306a36Sopenharmony_ci			   PKTSHIFT_V(CSIO_SGE_RX_DMA_OFFSET));
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	csio_hw_tp_wr_bits_indirect(hw, TP_INGRESS_CONFIG_A,
142862306a36Sopenharmony_ci				    CSUM_HAS_PSEUDO_HDR_F, 0);
142962306a36Sopenharmony_ci}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_cistatic void
143262306a36Sopenharmony_cicsio_init_intr_coalesce_parms(struct csio_hw *hw)
143362306a36Sopenharmony_ci{
143462306a36Sopenharmony_ci	struct csio_wrm *wrm = csio_hw_to_wrm(hw);
143562306a36Sopenharmony_ci	struct csio_sge *sge = &wrm->sge;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	csio_sge_thresh_reg = csio_closest_thresh(sge, csio_intr_coalesce_cnt);
143862306a36Sopenharmony_ci	if (csio_intr_coalesce_cnt) {
143962306a36Sopenharmony_ci		csio_sge_thresh_reg = 0;
144062306a36Sopenharmony_ci		csio_sge_timer_reg = X_TIMERREG_RESTART_COUNTER;
144162306a36Sopenharmony_ci		return;
144262306a36Sopenharmony_ci	}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	csio_sge_timer_reg = csio_closest_timer(sge, csio_intr_coalesce_time);
144562306a36Sopenharmony_ci}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci/*
144862306a36Sopenharmony_ci * csio_wr_get_sge - Get SGE register values.
144962306a36Sopenharmony_ci * @hw: HW module.
145062306a36Sopenharmony_ci *
145162306a36Sopenharmony_ci * Used by non-master functions and by master-functions relying on config file.
145262306a36Sopenharmony_ci */
145362306a36Sopenharmony_cistatic void
145462306a36Sopenharmony_cicsio_wr_get_sge(struct csio_hw *hw)
145562306a36Sopenharmony_ci{
145662306a36Sopenharmony_ci	struct csio_wrm *wrm = csio_hw_to_wrm(hw);
145762306a36Sopenharmony_ci	struct csio_sge *sge = &wrm->sge;
145862306a36Sopenharmony_ci	uint32_t ingpad;
145962306a36Sopenharmony_ci	int i;
146062306a36Sopenharmony_ci	u32 timer_value_0_and_1, timer_value_2_and_3, timer_value_4_and_5;
146162306a36Sopenharmony_ci	u32 ingress_rx_threshold;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL_A);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	ingpad = INGPADBOUNDARY_G(sge->sge_control);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	switch (ingpad) {
146862306a36Sopenharmony_ci	case X_INGPCIEBOUNDARY_32B:
146962306a36Sopenharmony_ci		sge->csio_fl_align = 32; break;
147062306a36Sopenharmony_ci	case X_INGPCIEBOUNDARY_64B:
147162306a36Sopenharmony_ci		sge->csio_fl_align = 64; break;
147262306a36Sopenharmony_ci	case X_INGPCIEBOUNDARY_128B:
147362306a36Sopenharmony_ci		sge->csio_fl_align = 128; break;
147462306a36Sopenharmony_ci	case X_INGPCIEBOUNDARY_256B:
147562306a36Sopenharmony_ci		sge->csio_fl_align = 256; break;
147662306a36Sopenharmony_ci	case X_INGPCIEBOUNDARY_512B:
147762306a36Sopenharmony_ci		sge->csio_fl_align = 512; break;
147862306a36Sopenharmony_ci	case X_INGPCIEBOUNDARY_1024B:
147962306a36Sopenharmony_ci		sge->csio_fl_align = 1024; break;
148062306a36Sopenharmony_ci	case X_INGPCIEBOUNDARY_2048B:
148162306a36Sopenharmony_ci		sge->csio_fl_align = 2048; break;
148262306a36Sopenharmony_ci	case X_INGPCIEBOUNDARY_4096B:
148362306a36Sopenharmony_ci		sge->csio_fl_align = 4096; break;
148462306a36Sopenharmony_ci	}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	for (i = 0; i < CSIO_SGE_FL_SIZE_REGS; i++)
148762306a36Sopenharmony_ci		csio_get_flbuf_size(hw, sge, i);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	timer_value_0_and_1 = csio_rd_reg32(hw, SGE_TIMER_VALUE_0_AND_1_A);
149062306a36Sopenharmony_ci	timer_value_2_and_3 = csio_rd_reg32(hw, SGE_TIMER_VALUE_2_AND_3_A);
149162306a36Sopenharmony_ci	timer_value_4_and_5 = csio_rd_reg32(hw, SGE_TIMER_VALUE_4_AND_5_A);
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	sge->timer_val[0] = (uint16_t)csio_core_ticks_to_us(hw,
149462306a36Sopenharmony_ci					TIMERVALUE0_G(timer_value_0_and_1));
149562306a36Sopenharmony_ci	sge->timer_val[1] = (uint16_t)csio_core_ticks_to_us(hw,
149662306a36Sopenharmony_ci					TIMERVALUE1_G(timer_value_0_and_1));
149762306a36Sopenharmony_ci	sge->timer_val[2] = (uint16_t)csio_core_ticks_to_us(hw,
149862306a36Sopenharmony_ci					TIMERVALUE2_G(timer_value_2_and_3));
149962306a36Sopenharmony_ci	sge->timer_val[3] = (uint16_t)csio_core_ticks_to_us(hw,
150062306a36Sopenharmony_ci					TIMERVALUE3_G(timer_value_2_and_3));
150162306a36Sopenharmony_ci	sge->timer_val[4] = (uint16_t)csio_core_ticks_to_us(hw,
150262306a36Sopenharmony_ci					TIMERVALUE4_G(timer_value_4_and_5));
150362306a36Sopenharmony_ci	sge->timer_val[5] = (uint16_t)csio_core_ticks_to_us(hw,
150462306a36Sopenharmony_ci					TIMERVALUE5_G(timer_value_4_and_5));
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	ingress_rx_threshold = csio_rd_reg32(hw, SGE_INGRESS_RX_THRESHOLD_A);
150762306a36Sopenharmony_ci	sge->counter_val[0] = THRESHOLD_0_G(ingress_rx_threshold);
150862306a36Sopenharmony_ci	sge->counter_val[1] = THRESHOLD_1_G(ingress_rx_threshold);
150962306a36Sopenharmony_ci	sge->counter_val[2] = THRESHOLD_2_G(ingress_rx_threshold);
151062306a36Sopenharmony_ci	sge->counter_val[3] = THRESHOLD_3_G(ingress_rx_threshold);
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	csio_init_intr_coalesce_parms(hw);
151362306a36Sopenharmony_ci}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci/*
151662306a36Sopenharmony_ci * csio_wr_set_sge - Initialize SGE registers
151762306a36Sopenharmony_ci * @hw: HW module.
151862306a36Sopenharmony_ci *
151962306a36Sopenharmony_ci * Used by Master function to initialize SGE registers in the absence
152062306a36Sopenharmony_ci * of a config file.
152162306a36Sopenharmony_ci */
152262306a36Sopenharmony_cistatic void
152362306a36Sopenharmony_cicsio_wr_set_sge(struct csio_hw *hw)
152462306a36Sopenharmony_ci{
152562306a36Sopenharmony_ci	struct csio_wrm *wrm = csio_hw_to_wrm(hw);
152662306a36Sopenharmony_ci	struct csio_sge *sge = &wrm->sge;
152762306a36Sopenharmony_ci	int i;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	/*
153062306a36Sopenharmony_ci	 * Set up our basic SGE mode to deliver CPL messages to our Ingress
153162306a36Sopenharmony_ci	 * Queue and Packet Date to the Free List.
153262306a36Sopenharmony_ci	 */
153362306a36Sopenharmony_ci	csio_set_reg_field(hw, SGE_CONTROL_A, RXPKTCPLMODE_F, RXPKTCPLMODE_F);
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL_A);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	/* sge->csio_fl_align is set up by csio_wr_fixup_host_params(). */
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	/*
154062306a36Sopenharmony_ci	 * Set up to drop DOORBELL writes when the DOORBELL FIFO overflows
154162306a36Sopenharmony_ci	 * and generate an interrupt when this occurs so we can recover.
154262306a36Sopenharmony_ci	 */
154362306a36Sopenharmony_ci	csio_set_reg_field(hw, SGE_DBFIFO_STATUS_A,
154462306a36Sopenharmony_ci			   LP_INT_THRESH_T5_V(LP_INT_THRESH_T5_M),
154562306a36Sopenharmony_ci			   LP_INT_THRESH_T5_V(CSIO_SGE_DBFIFO_INT_THRESH));
154662306a36Sopenharmony_ci	csio_set_reg_field(hw, SGE_DBFIFO_STATUS2_A,
154762306a36Sopenharmony_ci			   HP_INT_THRESH_T5_V(LP_INT_THRESH_T5_M),
154862306a36Sopenharmony_ci			   HP_INT_THRESH_T5_V(CSIO_SGE_DBFIFO_INT_THRESH));
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	csio_set_reg_field(hw, SGE_DOORBELL_CONTROL_A, ENABLE_DROP_F,
155162306a36Sopenharmony_ci			   ENABLE_DROP_F);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	/* SGE_FL_BUFFER_SIZE0 is set up by csio_wr_fixup_host_params(). */
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	CSIO_SET_FLBUF_SIZE(hw, 1, CSIO_SGE_FLBUF_SIZE1);
155662306a36Sopenharmony_ci	csio_wr_reg32(hw, (CSIO_SGE_FLBUF_SIZE2 + sge->csio_fl_align - 1)
155762306a36Sopenharmony_ci		      & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE2_A);
155862306a36Sopenharmony_ci	csio_wr_reg32(hw, (CSIO_SGE_FLBUF_SIZE3 + sge->csio_fl_align - 1)
155962306a36Sopenharmony_ci		      & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE3_A);
156062306a36Sopenharmony_ci	CSIO_SET_FLBUF_SIZE(hw, 4, CSIO_SGE_FLBUF_SIZE4);
156162306a36Sopenharmony_ci	CSIO_SET_FLBUF_SIZE(hw, 5, CSIO_SGE_FLBUF_SIZE5);
156262306a36Sopenharmony_ci	CSIO_SET_FLBUF_SIZE(hw, 6, CSIO_SGE_FLBUF_SIZE6);
156362306a36Sopenharmony_ci	CSIO_SET_FLBUF_SIZE(hw, 7, CSIO_SGE_FLBUF_SIZE7);
156462306a36Sopenharmony_ci	CSIO_SET_FLBUF_SIZE(hw, 8, CSIO_SGE_FLBUF_SIZE8);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	for (i = 0; i < CSIO_SGE_FL_SIZE_REGS; i++)
156762306a36Sopenharmony_ci		csio_get_flbuf_size(hw, sge, i);
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	/* Initialize interrupt coalescing attributes */
157062306a36Sopenharmony_ci	sge->timer_val[0] = CSIO_SGE_TIMER_VAL_0;
157162306a36Sopenharmony_ci	sge->timer_val[1] = CSIO_SGE_TIMER_VAL_1;
157262306a36Sopenharmony_ci	sge->timer_val[2] = CSIO_SGE_TIMER_VAL_2;
157362306a36Sopenharmony_ci	sge->timer_val[3] = CSIO_SGE_TIMER_VAL_3;
157462306a36Sopenharmony_ci	sge->timer_val[4] = CSIO_SGE_TIMER_VAL_4;
157562306a36Sopenharmony_ci	sge->timer_val[5] = CSIO_SGE_TIMER_VAL_5;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	sge->counter_val[0] = CSIO_SGE_INT_CNT_VAL_0;
157862306a36Sopenharmony_ci	sge->counter_val[1] = CSIO_SGE_INT_CNT_VAL_1;
157962306a36Sopenharmony_ci	sge->counter_val[2] = CSIO_SGE_INT_CNT_VAL_2;
158062306a36Sopenharmony_ci	sge->counter_val[3] = CSIO_SGE_INT_CNT_VAL_3;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	csio_wr_reg32(hw, THRESHOLD_0_V(sge->counter_val[0]) |
158362306a36Sopenharmony_ci		      THRESHOLD_1_V(sge->counter_val[1]) |
158462306a36Sopenharmony_ci		      THRESHOLD_2_V(sge->counter_val[2]) |
158562306a36Sopenharmony_ci		      THRESHOLD_3_V(sge->counter_val[3]),
158662306a36Sopenharmony_ci		      SGE_INGRESS_RX_THRESHOLD_A);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	csio_wr_reg32(hw,
158962306a36Sopenharmony_ci		   TIMERVALUE0_V(csio_us_to_core_ticks(hw, sge->timer_val[0])) |
159062306a36Sopenharmony_ci		   TIMERVALUE1_V(csio_us_to_core_ticks(hw, sge->timer_val[1])),
159162306a36Sopenharmony_ci		   SGE_TIMER_VALUE_0_AND_1_A);
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	csio_wr_reg32(hw,
159462306a36Sopenharmony_ci		   TIMERVALUE2_V(csio_us_to_core_ticks(hw, sge->timer_val[2])) |
159562306a36Sopenharmony_ci		   TIMERVALUE3_V(csio_us_to_core_ticks(hw, sge->timer_val[3])),
159662306a36Sopenharmony_ci		   SGE_TIMER_VALUE_2_AND_3_A);
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	csio_wr_reg32(hw,
159962306a36Sopenharmony_ci		   TIMERVALUE4_V(csio_us_to_core_ticks(hw, sge->timer_val[4])) |
160062306a36Sopenharmony_ci		   TIMERVALUE5_V(csio_us_to_core_ticks(hw, sge->timer_val[5])),
160162306a36Sopenharmony_ci		   SGE_TIMER_VALUE_4_AND_5_A);
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	csio_init_intr_coalesce_parms(hw);
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_civoid
160762306a36Sopenharmony_cicsio_wr_sge_init(struct csio_hw *hw)
160862306a36Sopenharmony_ci{
160962306a36Sopenharmony_ci	/*
161062306a36Sopenharmony_ci	 * If we are master and chip is not initialized:
161162306a36Sopenharmony_ci	 *    - If we plan to use the config file, we need to fixup some
161262306a36Sopenharmony_ci	 *      host specific registers, and read the rest of the SGE
161362306a36Sopenharmony_ci	 *      configuration.
161462306a36Sopenharmony_ci	 *    - If we dont plan to use the config file, we need to initialize
161562306a36Sopenharmony_ci	 *      SGE entirely, including fixing the host specific registers.
161662306a36Sopenharmony_ci	 * If we are master and chip is initialized, just read and work off of
161762306a36Sopenharmony_ci	 *	the already initialized SGE values.
161862306a36Sopenharmony_ci	 * If we arent the master, we are only allowed to read and work off of
161962306a36Sopenharmony_ci	 *      the already initialized SGE values.
162062306a36Sopenharmony_ci	 *
162162306a36Sopenharmony_ci	 * Therefore, before calling this function, we assume that the master-
162262306a36Sopenharmony_ci	 * ship of the card, state and whether to use config file or not, have
162362306a36Sopenharmony_ci	 * already been decided.
162462306a36Sopenharmony_ci	 */
162562306a36Sopenharmony_ci	if (csio_is_hw_master(hw)) {
162662306a36Sopenharmony_ci		if (hw->fw_state != CSIO_DEV_STATE_INIT)
162762306a36Sopenharmony_ci			csio_wr_fixup_host_params(hw);
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci		if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS)
163062306a36Sopenharmony_ci			csio_wr_get_sge(hw);
163162306a36Sopenharmony_ci		else
163262306a36Sopenharmony_ci			csio_wr_set_sge(hw);
163362306a36Sopenharmony_ci	} else
163462306a36Sopenharmony_ci		csio_wr_get_sge(hw);
163562306a36Sopenharmony_ci}
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci/*
163862306a36Sopenharmony_ci * csio_wrm_init - Initialize Work request module.
163962306a36Sopenharmony_ci * @wrm: WR module
164062306a36Sopenharmony_ci * @hw: HW pointer
164162306a36Sopenharmony_ci *
164262306a36Sopenharmony_ci * Allocates memory for an array of queue pointers starting at q_arr.
164362306a36Sopenharmony_ci */
164462306a36Sopenharmony_ciint
164562306a36Sopenharmony_cicsio_wrm_init(struct csio_wrm *wrm, struct csio_hw *hw)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	int i;
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	if (!wrm->num_q) {
165062306a36Sopenharmony_ci		csio_err(hw, "Num queues is not set\n");
165162306a36Sopenharmony_ci		return -EINVAL;
165262306a36Sopenharmony_ci	}
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	wrm->q_arr = kcalloc(wrm->num_q, sizeof(struct csio_q *), GFP_KERNEL);
165562306a36Sopenharmony_ci	if (!wrm->q_arr)
165662306a36Sopenharmony_ci		goto err;
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	for (i = 0; i < wrm->num_q; i++) {
165962306a36Sopenharmony_ci		wrm->q_arr[i] = kzalloc(sizeof(struct csio_q), GFP_KERNEL);
166062306a36Sopenharmony_ci		if (!wrm->q_arr[i]) {
166162306a36Sopenharmony_ci			while (--i >= 0)
166262306a36Sopenharmony_ci				kfree(wrm->q_arr[i]);
166362306a36Sopenharmony_ci			goto err_free_arr;
166462306a36Sopenharmony_ci		}
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci	wrm->free_qidx	= 0;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	return 0;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_cierr_free_arr:
167162306a36Sopenharmony_ci	kfree(wrm->q_arr);
167262306a36Sopenharmony_cierr:
167362306a36Sopenharmony_ci	return -ENOMEM;
167462306a36Sopenharmony_ci}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci/*
167762306a36Sopenharmony_ci * csio_wrm_exit - Initialize Work request module.
167862306a36Sopenharmony_ci * @wrm: WR module
167962306a36Sopenharmony_ci * @hw: HW module
168062306a36Sopenharmony_ci *
168162306a36Sopenharmony_ci * Uninitialize WR module. Free q_arr and pointers in it.
168262306a36Sopenharmony_ci * We have the additional job of freeing the DMA memory associated
168362306a36Sopenharmony_ci * with the queues.
168462306a36Sopenharmony_ci */
168562306a36Sopenharmony_civoid
168662306a36Sopenharmony_cicsio_wrm_exit(struct csio_wrm *wrm, struct csio_hw *hw)
168762306a36Sopenharmony_ci{
168862306a36Sopenharmony_ci	int i;
168962306a36Sopenharmony_ci	uint32_t j;
169062306a36Sopenharmony_ci	struct csio_q *q;
169162306a36Sopenharmony_ci	struct csio_dma_buf *buf;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	for (i = 0; i < wrm->num_q; i++) {
169462306a36Sopenharmony_ci		q = wrm->q_arr[i];
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci		if (wrm->free_qidx && (i < wrm->free_qidx)) {
169762306a36Sopenharmony_ci			if (q->type == CSIO_FREELIST) {
169862306a36Sopenharmony_ci				if (!q->un.fl.bufs)
169962306a36Sopenharmony_ci					continue;
170062306a36Sopenharmony_ci				for (j = 0; j < q->credits; j++) {
170162306a36Sopenharmony_ci					buf = &q->un.fl.bufs[j];
170262306a36Sopenharmony_ci					if (!buf->vaddr)
170362306a36Sopenharmony_ci						continue;
170462306a36Sopenharmony_ci					dma_free_coherent(&hw->pdev->dev,
170562306a36Sopenharmony_ci							buf->len, buf->vaddr,
170662306a36Sopenharmony_ci							buf->paddr);
170762306a36Sopenharmony_ci				}
170862306a36Sopenharmony_ci				kfree(q->un.fl.bufs);
170962306a36Sopenharmony_ci			}
171062306a36Sopenharmony_ci			dma_free_coherent(&hw->pdev->dev, q->size,
171162306a36Sopenharmony_ci					q->vstart, q->pstart);
171262306a36Sopenharmony_ci		}
171362306a36Sopenharmony_ci		kfree(q);
171462306a36Sopenharmony_ci	}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	hw->flags &= ~CSIO_HWF_Q_MEM_ALLOCED;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	kfree(wrm->q_arr);
171962306a36Sopenharmony_ci}
1720