162306a36Sopenharmony_ci/**********************************************************************
262306a36Sopenharmony_ci * Author: Cavium, Inc.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Contact: support@cavium.com
562306a36Sopenharmony_ci *          Please include "LiquidIO" in the subject.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2003-2016 Cavium, Inc.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This file is free software; you can redistribute it and/or modify
1062306a36Sopenharmony_ci * it under the terms of the GNU General Public License, Version 2, as
1162306a36Sopenharmony_ci * published by the Free Software Foundation.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * This file is distributed in the hope that it will be useful, but
1462306a36Sopenharmony_ci * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
1562306a36Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
1662306a36Sopenharmony_ci * NONINFRINGEMENT.  See the GNU General Public License for more
1762306a36Sopenharmony_ci * details.
1862306a36Sopenharmony_ci **********************************************************************/
1962306a36Sopenharmony_ci#include <linux/pci.h>
2062306a36Sopenharmony_ci#include <linux/netdevice.h>
2162306a36Sopenharmony_ci#include <linux/vmalloc.h>
2262306a36Sopenharmony_ci#include "liquidio_common.h"
2362306a36Sopenharmony_ci#include "octeon_droq.h"
2462306a36Sopenharmony_ci#include "octeon_iq.h"
2562306a36Sopenharmony_ci#include "response_manager.h"
2662306a36Sopenharmony_ci#include "octeon_device.h"
2762306a36Sopenharmony_ci#include "octeon_main.h"
2862306a36Sopenharmony_ci#include "octeon_network.h"
2962306a36Sopenharmony_ci#include "cn66xx_device.h"
3062306a36Sopenharmony_ci#include "cn23xx_pf_device.h"
3162306a36Sopenharmony_ci#include "cn23xx_vf_device.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct iq_post_status {
3462306a36Sopenharmony_ci	int status;
3562306a36Sopenharmony_ci	int index;
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void check_db_timeout(struct work_struct *work);
3962306a36Sopenharmony_cistatic void  __check_db_timeout(struct octeon_device *oct, u64 iq_no);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void (*reqtype_free_fn[MAX_OCTEON_DEVICES][REQTYPE_LAST + 1]) (void *);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* Define this to return the request status comaptible to old code */
4462306a36Sopenharmony_ci/*#define OCTEON_USE_OLD_REQ_STATUS*/
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* Return 0 on success, 1 on failure */
4762306a36Sopenharmony_ciint octeon_init_instr_queue(struct octeon_device *oct,
4862306a36Sopenharmony_ci			    union oct_txpciq txpciq,
4962306a36Sopenharmony_ci			    u32 num_descs)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct octeon_instr_queue *iq;
5262306a36Sopenharmony_ci	struct octeon_iq_config *conf = NULL;
5362306a36Sopenharmony_ci	u32 iq_no = (u32)txpciq.s.q_no;
5462306a36Sopenharmony_ci	u32 q_size;
5562306a36Sopenharmony_ci	struct cavium_wq *db_wq;
5662306a36Sopenharmony_ci	int numa_node = dev_to_node(&oct->pci_dev->dev);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (OCTEON_CN6XXX(oct))
5962306a36Sopenharmony_ci		conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn6xxx)));
6062306a36Sopenharmony_ci	else if (OCTEON_CN23XX_PF(oct))
6162306a36Sopenharmony_ci		conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn23xx_pf)));
6262306a36Sopenharmony_ci	else if (OCTEON_CN23XX_VF(oct))
6362306a36Sopenharmony_ci		conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn23xx_vf)));
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (!conf) {
6662306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Unsupported Chip %x\n",
6762306a36Sopenharmony_ci			oct->chip_id);
6862306a36Sopenharmony_ci		return 1;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	q_size = (u32)conf->instr_type * num_descs;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	iq = oct->instr_queue[iq_no];
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	iq->oct_dev = oct;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	iq->base_addr = lio_dma_alloc(oct, q_size, &iq->base_addr_dma);
7862306a36Sopenharmony_ci	if (!iq->base_addr) {
7962306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Cannot allocate memory for instr queue %d\n",
8062306a36Sopenharmony_ci			iq_no);
8162306a36Sopenharmony_ci		return 1;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	iq->max_count = num_descs;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* Initialize a list to holds requests that have been posted to Octeon
8762306a36Sopenharmony_ci	 * but has yet to be fetched by octeon
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	iq->request_list = vzalloc_node(array_size(num_descs, sizeof(*iq->request_list)),
9062306a36Sopenharmony_ci					numa_node);
9162306a36Sopenharmony_ci	if (!iq->request_list)
9262306a36Sopenharmony_ci		iq->request_list = vzalloc(array_size(num_descs, sizeof(*iq->request_list)));
9362306a36Sopenharmony_ci	if (!iq->request_list) {
9462306a36Sopenharmony_ci		lio_dma_free(oct, q_size, iq->base_addr, iq->base_addr_dma);
9562306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Alloc failed for IQ[%d] nr free list\n",
9662306a36Sopenharmony_ci			iq_no);
9762306a36Sopenharmony_ci		return 1;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	dev_dbg(&oct->pci_dev->dev, "IQ[%d]: base: %p basedma: %pad count: %d\n",
10162306a36Sopenharmony_ci		iq_no, iq->base_addr, &iq->base_addr_dma, iq->max_count);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	iq->txpciq.u64 = txpciq.u64;
10462306a36Sopenharmony_ci	iq->fill_threshold = (u32)conf->db_min;
10562306a36Sopenharmony_ci	iq->fill_cnt = 0;
10662306a36Sopenharmony_ci	iq->host_write_index = 0;
10762306a36Sopenharmony_ci	iq->octeon_read_index = 0;
10862306a36Sopenharmony_ci	iq->flush_index = 0;
10962306a36Sopenharmony_ci	iq->last_db_time = 0;
11062306a36Sopenharmony_ci	iq->do_auto_flush = 1;
11162306a36Sopenharmony_ci	iq->db_timeout = (u32)conf->db_timeout;
11262306a36Sopenharmony_ci	atomic_set(&iq->instr_pending, 0);
11362306a36Sopenharmony_ci	iq->pkts_processed = 0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* Initialize the spinlock for this instruction queue */
11662306a36Sopenharmony_ci	spin_lock_init(&iq->lock);
11762306a36Sopenharmony_ci	if (iq_no == 0) {
11862306a36Sopenharmony_ci		iq->allow_soft_cmds = true;
11962306a36Sopenharmony_ci		spin_lock_init(&iq->post_lock);
12062306a36Sopenharmony_ci	} else {
12162306a36Sopenharmony_ci		iq->allow_soft_cmds = false;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	spin_lock_init(&iq->iq_flush_running_lock);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	oct->io_qmask.iq |= BIT_ULL(iq_no);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* Set the 32B/64B mode for each input queue */
12962306a36Sopenharmony_ci	oct->io_qmask.iq64B |= ((conf->instr_type == 64) << iq_no);
13062306a36Sopenharmony_ci	iq->iqcmd_64B = (conf->instr_type == 64);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	oct->fn_list.setup_iq_regs(oct, iq_no);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	oct->check_db_wq[iq_no].wq = alloc_workqueue("check_iq_db",
13562306a36Sopenharmony_ci						     WQ_MEM_RECLAIM,
13662306a36Sopenharmony_ci						     0);
13762306a36Sopenharmony_ci	if (!oct->check_db_wq[iq_no].wq) {
13862306a36Sopenharmony_ci		vfree(iq->request_list);
13962306a36Sopenharmony_ci		iq->request_list = NULL;
14062306a36Sopenharmony_ci		lio_dma_free(oct, q_size, iq->base_addr, iq->base_addr_dma);
14162306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "check db wq create failed for iq %d\n",
14262306a36Sopenharmony_ci			iq_no);
14362306a36Sopenharmony_ci		return 1;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	db_wq = &oct->check_db_wq[iq_no];
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	INIT_DELAYED_WORK(&db_wq->wk.work, check_db_timeout);
14962306a36Sopenharmony_ci	db_wq->wk.ctxptr = oct;
15062306a36Sopenharmony_ci	db_wq->wk.ctxul = iq_no;
15162306a36Sopenharmony_ci	queue_delayed_work(db_wq->wq, &db_wq->wk.work, msecs_to_jiffies(1));
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ciint octeon_delete_instr_queue(struct octeon_device *oct, u32 iq_no)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	u64 desc_size = 0, q_size;
15962306a36Sopenharmony_ci	struct octeon_instr_queue *iq = oct->instr_queue[iq_no];
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	cancel_delayed_work_sync(&oct->check_db_wq[iq_no].wk.work);
16262306a36Sopenharmony_ci	destroy_workqueue(oct->check_db_wq[iq_no].wq);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (OCTEON_CN6XXX(oct))
16562306a36Sopenharmony_ci		desc_size =
16662306a36Sopenharmony_ci		    CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn6xxx));
16762306a36Sopenharmony_ci	else if (OCTEON_CN23XX_PF(oct))
16862306a36Sopenharmony_ci		desc_size =
16962306a36Sopenharmony_ci		    CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn23xx_pf));
17062306a36Sopenharmony_ci	else if (OCTEON_CN23XX_VF(oct))
17162306a36Sopenharmony_ci		desc_size =
17262306a36Sopenharmony_ci		    CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn23xx_vf));
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	vfree(iq->request_list);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (iq->base_addr) {
17762306a36Sopenharmony_ci		q_size = iq->max_count * desc_size;
17862306a36Sopenharmony_ci		lio_dma_free(oct, (u32)q_size, iq->base_addr,
17962306a36Sopenharmony_ci			     iq->base_addr_dma);
18062306a36Sopenharmony_ci		oct->io_qmask.iq &= ~(1ULL << iq_no);
18162306a36Sopenharmony_ci		vfree(oct->instr_queue[iq_no]);
18262306a36Sopenharmony_ci		oct->instr_queue[iq_no] = NULL;
18362306a36Sopenharmony_ci		oct->num_iqs--;
18462306a36Sopenharmony_ci		return 0;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	return 1;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_delete_instr_queue);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/* Return 0 on success, 1 on failure */
19162306a36Sopenharmony_ciint octeon_setup_iq(struct octeon_device *oct,
19262306a36Sopenharmony_ci		    int ifidx,
19362306a36Sopenharmony_ci		    int q_index,
19462306a36Sopenharmony_ci		    union oct_txpciq txpciq,
19562306a36Sopenharmony_ci		    u32 num_descs,
19662306a36Sopenharmony_ci		    void *app_ctx)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	u32 iq_no = (u32)txpciq.s.q_no;
19962306a36Sopenharmony_ci	int numa_node = dev_to_node(&oct->pci_dev->dev);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (oct->instr_queue[iq_no]) {
20262306a36Sopenharmony_ci		dev_dbg(&oct->pci_dev->dev, "IQ is in use. Cannot create the IQ: %d again\n",
20362306a36Sopenharmony_ci			iq_no);
20462306a36Sopenharmony_ci		oct->instr_queue[iq_no]->txpciq.u64 = txpciq.u64;
20562306a36Sopenharmony_ci		oct->instr_queue[iq_no]->app_ctx = app_ctx;
20662306a36Sopenharmony_ci		return 0;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	oct->instr_queue[iq_no] =
20962306a36Sopenharmony_ci	    vzalloc_node(sizeof(struct octeon_instr_queue), numa_node);
21062306a36Sopenharmony_ci	if (!oct->instr_queue[iq_no])
21162306a36Sopenharmony_ci		oct->instr_queue[iq_no] =
21262306a36Sopenharmony_ci		    vzalloc(sizeof(struct octeon_instr_queue));
21362306a36Sopenharmony_ci	if (!oct->instr_queue[iq_no])
21462306a36Sopenharmony_ci		return 1;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	oct->instr_queue[iq_no]->q_index = q_index;
21862306a36Sopenharmony_ci	oct->instr_queue[iq_no]->app_ctx = app_ctx;
21962306a36Sopenharmony_ci	oct->instr_queue[iq_no]->ifidx = ifidx;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (octeon_init_instr_queue(oct, txpciq, num_descs)) {
22262306a36Sopenharmony_ci		vfree(oct->instr_queue[iq_no]);
22362306a36Sopenharmony_ci		oct->instr_queue[iq_no] = NULL;
22462306a36Sopenharmony_ci		return 1;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	oct->num_iqs++;
22862306a36Sopenharmony_ci	if (oct->fn_list.enable_io_queues(oct)) {
22962306a36Sopenharmony_ci		octeon_delete_instr_queue(oct, iq_no);
23062306a36Sopenharmony_ci		return 1;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return 0;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ciint lio_wait_for_instr_fetch(struct octeon_device *oct)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	int i, retry = 1000, pending, instr_cnt = 0;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	do {
24162306a36Sopenharmony_ci		instr_cnt = 0;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) {
24462306a36Sopenharmony_ci			if (!(oct->io_qmask.iq & BIT_ULL(i)))
24562306a36Sopenharmony_ci				continue;
24662306a36Sopenharmony_ci			pending =
24762306a36Sopenharmony_ci			    atomic_read(&oct->instr_queue[i]->instr_pending);
24862306a36Sopenharmony_ci			if (pending)
24962306a36Sopenharmony_ci				__check_db_timeout(oct, i);
25062306a36Sopenharmony_ci			instr_cnt += pending;
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		if (instr_cnt == 0)
25462306a36Sopenharmony_ci			break;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		schedule_timeout_uninterruptible(1);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	} while (retry-- && instr_cnt);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return instr_cnt;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lio_wait_for_instr_fetch);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic inline void
26562306a36Sopenharmony_ciring_doorbell(struct octeon_device *oct, struct octeon_instr_queue *iq)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	if (atomic_read(&oct->status) == OCT_DEV_RUNNING) {
26862306a36Sopenharmony_ci		writel(iq->fill_cnt, iq->doorbell_reg);
26962306a36Sopenharmony_ci		/* make sure doorbell write goes through */
27062306a36Sopenharmony_ci		iq->fill_cnt = 0;
27162306a36Sopenharmony_ci		iq->last_db_time = jiffies;
27262306a36Sopenharmony_ci		return;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_civoid
27762306a36Sopenharmony_ciocteon_ring_doorbell_locked(struct octeon_device *oct, u32 iq_no)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct octeon_instr_queue *iq;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	iq = oct->instr_queue[iq_no];
28262306a36Sopenharmony_ci	spin_lock(&iq->post_lock);
28362306a36Sopenharmony_ci	if (iq->fill_cnt)
28462306a36Sopenharmony_ci		ring_doorbell(oct, iq);
28562306a36Sopenharmony_ci	spin_unlock(&iq->post_lock);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_ring_doorbell_locked);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic inline void __copy_cmd_into_iq(struct octeon_instr_queue *iq,
29062306a36Sopenharmony_ci				      u8 *cmd)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	u8 *iqptr, cmdsize;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	cmdsize = ((iq->iqcmd_64B) ? 64 : 32);
29562306a36Sopenharmony_ci	iqptr = iq->base_addr + (cmdsize * iq->host_write_index);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	memcpy(iqptr, cmd, cmdsize);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic inline struct iq_post_status
30162306a36Sopenharmony_ci__post_command2(struct octeon_instr_queue *iq, u8 *cmd)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct iq_post_status st;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	st.status = IQ_SEND_OK;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* This ensures that the read index does not wrap around to the same
30862306a36Sopenharmony_ci	 * position if queue gets full before Octeon could fetch any instr.
30962306a36Sopenharmony_ci	 */
31062306a36Sopenharmony_ci	if (atomic_read(&iq->instr_pending) >= (s32)(iq->max_count - 1)) {
31162306a36Sopenharmony_ci		st.status = IQ_SEND_FAILED;
31262306a36Sopenharmony_ci		st.index = -1;
31362306a36Sopenharmony_ci		return st;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (atomic_read(&iq->instr_pending) >= (s32)(iq->max_count - 2))
31762306a36Sopenharmony_ci		st.status = IQ_SEND_STOP;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	__copy_cmd_into_iq(iq, cmd);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* "index" is returned, host_write_index is modified. */
32262306a36Sopenharmony_ci	st.index = iq->host_write_index;
32362306a36Sopenharmony_ci	iq->host_write_index = incr_index(iq->host_write_index, 1,
32462306a36Sopenharmony_ci					  iq->max_count);
32562306a36Sopenharmony_ci	iq->fill_cnt++;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* Flush the command into memory. We need to be sure the data is in
32862306a36Sopenharmony_ci	 * memory before indicating that the instruction is pending.
32962306a36Sopenharmony_ci	 */
33062306a36Sopenharmony_ci	wmb();
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	atomic_inc(&iq->instr_pending);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return st;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ciint
33862306a36Sopenharmony_ciocteon_register_reqtype_free_fn(struct octeon_device *oct, int reqtype,
33962306a36Sopenharmony_ci				void (*fn)(void *))
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	if (reqtype > REQTYPE_LAST) {
34262306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "%s: Invalid reqtype: %d\n",
34362306a36Sopenharmony_ci			__func__, reqtype);
34462306a36Sopenharmony_ci		return -EINVAL;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	reqtype_free_fn[oct->octeon_id][reqtype] = fn;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return 0;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_register_reqtype_free_fn);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic inline void
35462306a36Sopenharmony_ci__add_to_request_list(struct octeon_instr_queue *iq,
35562306a36Sopenharmony_ci		      int idx, void *buf, int reqtype)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	iq->request_list[idx].buf = buf;
35862306a36Sopenharmony_ci	iq->request_list[idx].reqtype = reqtype;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci/* Can only run in process context */
36262306a36Sopenharmony_ciint
36362306a36Sopenharmony_cilio_process_iq_request_list(struct octeon_device *oct,
36462306a36Sopenharmony_ci			    struct octeon_instr_queue *iq, u32 napi_budget)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct cavium_wq *cwq = &oct->dma_comp_wq;
36762306a36Sopenharmony_ci	int reqtype;
36862306a36Sopenharmony_ci	void *buf;
36962306a36Sopenharmony_ci	u32 old = iq->flush_index;
37062306a36Sopenharmony_ci	u32 inst_count = 0;
37162306a36Sopenharmony_ci	unsigned int pkts_compl = 0, bytes_compl = 0;
37262306a36Sopenharmony_ci	struct octeon_soft_command *sc;
37362306a36Sopenharmony_ci	unsigned long flags;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	while (old != iq->octeon_read_index) {
37662306a36Sopenharmony_ci		reqtype = iq->request_list[old].reqtype;
37762306a36Sopenharmony_ci		buf     = iq->request_list[old].buf;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		if (reqtype == REQTYPE_NONE)
38062306a36Sopenharmony_ci			goto skip_this;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		octeon_update_tx_completion_counters(buf, reqtype, &pkts_compl,
38362306a36Sopenharmony_ci						     &bytes_compl);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		switch (reqtype) {
38662306a36Sopenharmony_ci		case REQTYPE_NORESP_NET:
38762306a36Sopenharmony_ci		case REQTYPE_NORESP_NET_SG:
38862306a36Sopenharmony_ci		case REQTYPE_RESP_NET_SG:
38962306a36Sopenharmony_ci			reqtype_free_fn[oct->octeon_id][reqtype](buf);
39062306a36Sopenharmony_ci			break;
39162306a36Sopenharmony_ci		case REQTYPE_RESP_NET:
39262306a36Sopenharmony_ci		case REQTYPE_SOFT_COMMAND:
39362306a36Sopenharmony_ci			sc = buf;
39462306a36Sopenharmony_ci			/* We're expecting a response from Octeon.
39562306a36Sopenharmony_ci			 * It's up to lio_process_ordered_list() to
39662306a36Sopenharmony_ci			 * process  sc. Add sc to the ordered soft
39762306a36Sopenharmony_ci			 * command response list because we expect
39862306a36Sopenharmony_ci			 * a response from Octeon.
39962306a36Sopenharmony_ci			 */
40062306a36Sopenharmony_ci			spin_lock_irqsave(&oct->response_list
40162306a36Sopenharmony_ci					  [OCTEON_ORDERED_SC_LIST].lock, flags);
40262306a36Sopenharmony_ci			atomic_inc(&oct->response_list
40362306a36Sopenharmony_ci				   [OCTEON_ORDERED_SC_LIST].pending_req_count);
40462306a36Sopenharmony_ci			list_add_tail(&sc->node, &oct->response_list
40562306a36Sopenharmony_ci				[OCTEON_ORDERED_SC_LIST].head);
40662306a36Sopenharmony_ci			spin_unlock_irqrestore(&oct->response_list
40762306a36Sopenharmony_ci					       [OCTEON_ORDERED_SC_LIST].lock,
40862306a36Sopenharmony_ci					       flags);
40962306a36Sopenharmony_ci			break;
41062306a36Sopenharmony_ci		default:
41162306a36Sopenharmony_ci			dev_err(&oct->pci_dev->dev,
41262306a36Sopenharmony_ci				"%s Unknown reqtype: %d buf: %p at idx %d\n",
41362306a36Sopenharmony_ci				__func__, reqtype, buf, old);
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		iq->request_list[old].buf = NULL;
41762306a36Sopenharmony_ci		iq->request_list[old].reqtype = 0;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci skip_this:
42062306a36Sopenharmony_ci		inst_count++;
42162306a36Sopenharmony_ci		old = incr_index(old, 1, iq->max_count);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		if ((napi_budget) && (inst_count >= napi_budget))
42462306a36Sopenharmony_ci			break;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci	if (bytes_compl)
42762306a36Sopenharmony_ci		octeon_report_tx_completion_to_bql(iq->app_ctx, pkts_compl,
42862306a36Sopenharmony_ci						   bytes_compl);
42962306a36Sopenharmony_ci	iq->flush_index = old;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (atomic_read(&oct->response_list
43262306a36Sopenharmony_ci			[OCTEON_ORDERED_SC_LIST].pending_req_count))
43362306a36Sopenharmony_ci		queue_work(cwq->wq, &cwq->wk.work.work);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	return inst_count;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lio_process_iq_request_list);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/* Can only be called from process context */
44062306a36Sopenharmony_ciint
44162306a36Sopenharmony_ciocteon_flush_iq(struct octeon_device *oct, struct octeon_instr_queue *iq,
44262306a36Sopenharmony_ci		u32 napi_budget)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	u32 inst_processed = 0;
44562306a36Sopenharmony_ci	u32 tot_inst_processed = 0;
44662306a36Sopenharmony_ci	int tx_done = 1;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (!spin_trylock(&iq->iq_flush_running_lock))
44962306a36Sopenharmony_ci		return tx_done;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	spin_lock_bh(&iq->lock);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	iq->octeon_read_index = oct->fn_list.update_iq_read_idx(iq);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	do {
45662306a36Sopenharmony_ci		/* Process any outstanding IQ packets. */
45762306a36Sopenharmony_ci		if (iq->flush_index == iq->octeon_read_index)
45862306a36Sopenharmony_ci			break;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		if (napi_budget)
46162306a36Sopenharmony_ci			inst_processed =
46262306a36Sopenharmony_ci				lio_process_iq_request_list(oct, iq,
46362306a36Sopenharmony_ci							    napi_budget -
46462306a36Sopenharmony_ci							    tot_inst_processed);
46562306a36Sopenharmony_ci		else
46662306a36Sopenharmony_ci			inst_processed =
46762306a36Sopenharmony_ci				lio_process_iq_request_list(oct, iq, 0);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		if (inst_processed) {
47062306a36Sopenharmony_ci			iq->pkts_processed += inst_processed;
47162306a36Sopenharmony_ci			atomic_sub(inst_processed, &iq->instr_pending);
47262306a36Sopenharmony_ci			iq->stats.instr_processed += inst_processed;
47362306a36Sopenharmony_ci		}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		tot_inst_processed += inst_processed;
47662306a36Sopenharmony_ci	} while (tot_inst_processed < napi_budget);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	if (napi_budget && (tot_inst_processed >= napi_budget))
47962306a36Sopenharmony_ci		tx_done = 0;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	iq->last_db_time = jiffies;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	spin_unlock_bh(&iq->lock);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	spin_unlock(&iq->iq_flush_running_lock);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	return tx_done;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci/* Process instruction queue after timeout.
49162306a36Sopenharmony_ci * This routine gets called from a workqueue or when removing the module.
49262306a36Sopenharmony_ci */
49362306a36Sopenharmony_cistatic void __check_db_timeout(struct octeon_device *oct, u64 iq_no)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct octeon_instr_queue *iq;
49662306a36Sopenharmony_ci	u64 next_time;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (!oct)
49962306a36Sopenharmony_ci		return;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	iq = oct->instr_queue[iq_no];
50262306a36Sopenharmony_ci	if (!iq)
50362306a36Sopenharmony_ci		return;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/* return immediately, if no work pending */
50662306a36Sopenharmony_ci	if (!atomic_read(&iq->instr_pending))
50762306a36Sopenharmony_ci		return;
50862306a36Sopenharmony_ci	/* If jiffies - last_db_time < db_timeout do nothing  */
50962306a36Sopenharmony_ci	next_time = iq->last_db_time + iq->db_timeout;
51062306a36Sopenharmony_ci	if (!time_after(jiffies, (unsigned long)next_time))
51162306a36Sopenharmony_ci		return;
51262306a36Sopenharmony_ci	iq->last_db_time = jiffies;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* Flush the instruction queue */
51562306a36Sopenharmony_ci	octeon_flush_iq(oct, iq, 0);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	lio_enable_irq(NULL, iq);
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci/* Called by the Poll thread at regular intervals to check the instruction
52162306a36Sopenharmony_ci * queue for commands to be posted and for commands that were fetched by Octeon.
52262306a36Sopenharmony_ci */
52362306a36Sopenharmony_cistatic void check_db_timeout(struct work_struct *work)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct cavium_wk *wk = (struct cavium_wk *)work;
52662306a36Sopenharmony_ci	struct octeon_device *oct = (struct octeon_device *)wk->ctxptr;
52762306a36Sopenharmony_ci	u64 iq_no = wk->ctxul;
52862306a36Sopenharmony_ci	struct cavium_wq *db_wq = &oct->check_db_wq[iq_no];
52962306a36Sopenharmony_ci	u32 delay = 10;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	__check_db_timeout(oct, iq_no);
53262306a36Sopenharmony_ci	queue_delayed_work(db_wq->wq, &db_wq->wk.work, msecs_to_jiffies(delay));
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ciint
53662306a36Sopenharmony_ciocteon_send_command(struct octeon_device *oct, u32 iq_no,
53762306a36Sopenharmony_ci		    u32 force_db, void *cmd, void *buf,
53862306a36Sopenharmony_ci		    u32 datasize, u32 reqtype)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	int xmit_stopped;
54162306a36Sopenharmony_ci	struct iq_post_status st;
54262306a36Sopenharmony_ci	struct octeon_instr_queue *iq = oct->instr_queue[iq_no];
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	/* Get the lock and prevent other tasks and tx interrupt handler from
54562306a36Sopenharmony_ci	 * running.
54662306a36Sopenharmony_ci	 */
54762306a36Sopenharmony_ci	if (iq->allow_soft_cmds)
54862306a36Sopenharmony_ci		spin_lock_bh(&iq->post_lock);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	st = __post_command2(iq, cmd);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (st.status != IQ_SEND_FAILED) {
55362306a36Sopenharmony_ci		xmit_stopped = octeon_report_sent_bytes_to_bql(buf, reqtype);
55462306a36Sopenharmony_ci		__add_to_request_list(iq, st.index, buf, reqtype);
55562306a36Sopenharmony_ci		INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, bytes_sent, datasize);
55662306a36Sopenharmony_ci		INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, instr_posted, 1);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		if (iq->fill_cnt >= MAX_OCTEON_FILL_COUNT || force_db ||
55962306a36Sopenharmony_ci		    xmit_stopped || st.status == IQ_SEND_STOP)
56062306a36Sopenharmony_ci			ring_doorbell(oct, iq);
56162306a36Sopenharmony_ci	} else {
56262306a36Sopenharmony_ci		INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, instr_dropped, 1);
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (iq->allow_soft_cmds)
56662306a36Sopenharmony_ci		spin_unlock_bh(&iq->post_lock);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* This is only done here to expedite packets being flushed
56962306a36Sopenharmony_ci	 * for cases where there are no IQ completion interrupts.
57062306a36Sopenharmony_ci	 */
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return st.status;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_send_command);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_civoid
57762306a36Sopenharmony_ciocteon_prepare_soft_command(struct octeon_device *oct,
57862306a36Sopenharmony_ci			    struct octeon_soft_command *sc,
57962306a36Sopenharmony_ci			    u8 opcode,
58062306a36Sopenharmony_ci			    u8 subcode,
58162306a36Sopenharmony_ci			    u32 irh_ossp,
58262306a36Sopenharmony_ci			    u64 ossp0,
58362306a36Sopenharmony_ci			    u64 ossp1)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct octeon_config *oct_cfg;
58662306a36Sopenharmony_ci	struct octeon_instr_ih2 *ih2;
58762306a36Sopenharmony_ci	struct octeon_instr_ih3 *ih3;
58862306a36Sopenharmony_ci	struct octeon_instr_pki_ih3 *pki_ih3;
58962306a36Sopenharmony_ci	struct octeon_instr_irh *irh;
59062306a36Sopenharmony_ci	struct octeon_instr_rdp *rdp;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	WARN_ON(opcode > 15);
59362306a36Sopenharmony_ci	WARN_ON(subcode > 127);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	oct_cfg = octeon_get_conf(oct);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) {
59862306a36Sopenharmony_ci		ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		ih3->pkind = oct->instr_queue[sc->iq_no]->txpciq.s.pkind;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		pki_ih3 = (struct octeon_instr_pki_ih3 *)&sc->cmd.cmd3.pki_ih3;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		pki_ih3->w           = 1;
60562306a36Sopenharmony_ci		pki_ih3->raw         = 1;
60662306a36Sopenharmony_ci		pki_ih3->utag        = 1;
60762306a36Sopenharmony_ci		pki_ih3->uqpg        =
60862306a36Sopenharmony_ci			oct->instr_queue[sc->iq_no]->txpciq.s.use_qpg;
60962306a36Sopenharmony_ci		pki_ih3->utt         = 1;
61062306a36Sopenharmony_ci		pki_ih3->tag     = LIO_CONTROL;
61162306a36Sopenharmony_ci		pki_ih3->tagtype = ATOMIC_TAG;
61262306a36Sopenharmony_ci		pki_ih3->qpg         =
61362306a36Sopenharmony_ci			oct->instr_queue[sc->iq_no]->txpciq.s.ctrl_qpg;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		pki_ih3->pm          = 0x7;
61662306a36Sopenharmony_ci		pki_ih3->sl          = 8;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		if (sc->datasize)
61962306a36Sopenharmony_ci			ih3->dlengsz = sc->datasize;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		irh            = (struct octeon_instr_irh *)&sc->cmd.cmd3.irh;
62262306a36Sopenharmony_ci		irh->opcode    = opcode;
62362306a36Sopenharmony_ci		irh->subcode   = subcode;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		/* opcode/subcode specific parameters (ossp) */
62662306a36Sopenharmony_ci		irh->ossp       = irh_ossp;
62762306a36Sopenharmony_ci		sc->cmd.cmd3.ossp[0] = ossp0;
62862306a36Sopenharmony_ci		sc->cmd.cmd3.ossp[1] = ossp1;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		if (sc->rdatasize) {
63162306a36Sopenharmony_ci			rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd3.rdp;
63262306a36Sopenharmony_ci			rdp->pcie_port = oct->pcie_port;
63362306a36Sopenharmony_ci			rdp->rlen      = sc->rdatasize;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci			irh->rflag =  1;
63662306a36Sopenharmony_ci			/*PKI IH3*/
63762306a36Sopenharmony_ci			/* pki_ih3 irh+ossp[0]+ossp[1]+rdp+rptr = 48 bytes */
63862306a36Sopenharmony_ci			ih3->fsz    = LIO_SOFTCMDRESP_IH3;
63962306a36Sopenharmony_ci		} else {
64062306a36Sopenharmony_ci			irh->rflag =  0;
64162306a36Sopenharmony_ci			/*PKI IH3*/
64262306a36Sopenharmony_ci			/* pki_h3 + irh + ossp[0] + ossp[1] = 32 bytes */
64362306a36Sopenharmony_ci			ih3->fsz    = LIO_PCICMD_O3;
64462306a36Sopenharmony_ci		}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	} else {
64762306a36Sopenharmony_ci		ih2          = (struct octeon_instr_ih2 *)&sc->cmd.cmd2.ih2;
64862306a36Sopenharmony_ci		ih2->tagtype = ATOMIC_TAG;
64962306a36Sopenharmony_ci		ih2->tag     = LIO_CONTROL;
65062306a36Sopenharmony_ci		ih2->raw     = 1;
65162306a36Sopenharmony_ci		ih2->grp     = CFG_GET_CTRL_Q_GRP(oct_cfg);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		if (sc->datasize) {
65462306a36Sopenharmony_ci			ih2->dlengsz = sc->datasize;
65562306a36Sopenharmony_ci			ih2->rs = 1;
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		irh            = (struct octeon_instr_irh *)&sc->cmd.cmd2.irh;
65962306a36Sopenharmony_ci		irh->opcode    = opcode;
66062306a36Sopenharmony_ci		irh->subcode   = subcode;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		/* opcode/subcode specific parameters (ossp) */
66362306a36Sopenharmony_ci		irh->ossp       = irh_ossp;
66462306a36Sopenharmony_ci		sc->cmd.cmd2.ossp[0] = ossp0;
66562306a36Sopenharmony_ci		sc->cmd.cmd2.ossp[1] = ossp1;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		if (sc->rdatasize) {
66862306a36Sopenharmony_ci			rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd2.rdp;
66962306a36Sopenharmony_ci			rdp->pcie_port = oct->pcie_port;
67062306a36Sopenharmony_ci			rdp->rlen      = sc->rdatasize;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci			irh->rflag =  1;
67362306a36Sopenharmony_ci			/* irh+ossp[0]+ossp[1]+rdp+rptr = 40 bytes */
67462306a36Sopenharmony_ci			ih2->fsz   = LIO_SOFTCMDRESP_IH2;
67562306a36Sopenharmony_ci		} else {
67662306a36Sopenharmony_ci			irh->rflag =  0;
67762306a36Sopenharmony_ci			/* irh + ossp[0] + ossp[1] = 24 bytes */
67862306a36Sopenharmony_ci			ih2->fsz   = LIO_PCICMD_O2;
67962306a36Sopenharmony_ci		}
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_prepare_soft_command);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ciint octeon_send_soft_command(struct octeon_device *oct,
68562306a36Sopenharmony_ci			     struct octeon_soft_command *sc)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	struct octeon_instr_queue *iq;
68862306a36Sopenharmony_ci	struct octeon_instr_ih2 *ih2;
68962306a36Sopenharmony_ci	struct octeon_instr_ih3 *ih3;
69062306a36Sopenharmony_ci	struct octeon_instr_irh *irh;
69162306a36Sopenharmony_ci	u32 len;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	iq = oct->instr_queue[sc->iq_no];
69462306a36Sopenharmony_ci	if (!iq->allow_soft_cmds) {
69562306a36Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Soft commands are not allowed on Queue %d\n",
69662306a36Sopenharmony_ci			sc->iq_no);
69762306a36Sopenharmony_ci		INCR_INSTRQUEUE_PKT_COUNT(oct, sc->iq_no, instr_dropped, 1);
69862306a36Sopenharmony_ci		return IQ_SEND_FAILED;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) {
70262306a36Sopenharmony_ci		ih3 =  (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3;
70362306a36Sopenharmony_ci		if (ih3->dlengsz) {
70462306a36Sopenharmony_ci			WARN_ON(!sc->dmadptr);
70562306a36Sopenharmony_ci			sc->cmd.cmd3.dptr = sc->dmadptr;
70662306a36Sopenharmony_ci		}
70762306a36Sopenharmony_ci		irh = (struct octeon_instr_irh *)&sc->cmd.cmd3.irh;
70862306a36Sopenharmony_ci		if (irh->rflag) {
70962306a36Sopenharmony_ci			WARN_ON(!sc->dmarptr);
71062306a36Sopenharmony_ci			WARN_ON(!sc->status_word);
71162306a36Sopenharmony_ci			*sc->status_word = COMPLETION_WORD_INIT;
71262306a36Sopenharmony_ci			sc->cmd.cmd3.rptr = sc->dmarptr;
71362306a36Sopenharmony_ci		}
71462306a36Sopenharmony_ci		len = (u32)ih3->dlengsz;
71562306a36Sopenharmony_ci	} else {
71662306a36Sopenharmony_ci		ih2 = (struct octeon_instr_ih2 *)&sc->cmd.cmd2.ih2;
71762306a36Sopenharmony_ci		if (ih2->dlengsz) {
71862306a36Sopenharmony_ci			WARN_ON(!sc->dmadptr);
71962306a36Sopenharmony_ci			sc->cmd.cmd2.dptr = sc->dmadptr;
72062306a36Sopenharmony_ci		}
72162306a36Sopenharmony_ci		irh = (struct octeon_instr_irh *)&sc->cmd.cmd2.irh;
72262306a36Sopenharmony_ci		if (irh->rflag) {
72362306a36Sopenharmony_ci			WARN_ON(!sc->dmarptr);
72462306a36Sopenharmony_ci			WARN_ON(!sc->status_word);
72562306a36Sopenharmony_ci			*sc->status_word = COMPLETION_WORD_INIT;
72662306a36Sopenharmony_ci			sc->cmd.cmd2.rptr = sc->dmarptr;
72762306a36Sopenharmony_ci		}
72862306a36Sopenharmony_ci		len = (u32)ih2->dlengsz;
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	sc->expiry_time = jiffies + msecs_to_jiffies(LIO_SC_MAX_TMO_MS);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	return (octeon_send_command(oct, sc->iq_no, 1, &sc->cmd, sc,
73462306a36Sopenharmony_ci				    len, REQTYPE_SOFT_COMMAND));
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_send_soft_command);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ciint octeon_setup_sc_buffer_pool(struct octeon_device *oct)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	int i;
74162306a36Sopenharmony_ci	u64 dma_addr;
74262306a36Sopenharmony_ci	struct octeon_soft_command *sc;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	INIT_LIST_HEAD(&oct->sc_buf_pool.head);
74562306a36Sopenharmony_ci	spin_lock_init(&oct->sc_buf_pool.lock);
74662306a36Sopenharmony_ci	atomic_set(&oct->sc_buf_pool.alloc_buf_count, 0);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	for (i = 0; i < MAX_SOFT_COMMAND_BUFFERS; i++) {
74962306a36Sopenharmony_ci		sc = (struct octeon_soft_command *)
75062306a36Sopenharmony_ci			lio_dma_alloc(oct,
75162306a36Sopenharmony_ci				      SOFT_COMMAND_BUFFER_SIZE,
75262306a36Sopenharmony_ci					  (dma_addr_t *)&dma_addr);
75362306a36Sopenharmony_ci		if (!sc) {
75462306a36Sopenharmony_ci			octeon_free_sc_buffer_pool(oct);
75562306a36Sopenharmony_ci			return 1;
75662306a36Sopenharmony_ci		}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		sc->dma_addr = dma_addr;
75962306a36Sopenharmony_ci		sc->size = SOFT_COMMAND_BUFFER_SIZE;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		list_add_tail(&sc->node, &oct->sc_buf_pool.head);
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	return 0;
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_setup_sc_buffer_pool);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ciint octeon_free_sc_done_list(struct octeon_device *oct)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	struct octeon_response_list *done_sc_list, *zombie_sc_list;
77162306a36Sopenharmony_ci	struct octeon_soft_command *sc;
77262306a36Sopenharmony_ci	struct list_head *tmp, *tmp2;
77362306a36Sopenharmony_ci	spinlock_t *sc_lists_lock; /* lock for response_list */
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	done_sc_list = &oct->response_list[OCTEON_DONE_SC_LIST];
77662306a36Sopenharmony_ci	zombie_sc_list = &oct->response_list[OCTEON_ZOMBIE_SC_LIST];
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	if (!atomic_read(&done_sc_list->pending_req_count))
77962306a36Sopenharmony_ci		return 0;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	sc_lists_lock = &oct->response_list[OCTEON_ORDERED_SC_LIST].lock;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	spin_lock_bh(sc_lists_lock);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	list_for_each_safe(tmp, tmp2, &done_sc_list->head) {
78662306a36Sopenharmony_ci		sc = list_entry(tmp, struct octeon_soft_command, node);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci		if (READ_ONCE(sc->caller_is_done)) {
78962306a36Sopenharmony_ci			list_del(&sc->node);
79062306a36Sopenharmony_ci			atomic_dec(&done_sc_list->pending_req_count);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci			if (*sc->status_word == COMPLETION_WORD_INIT) {
79362306a36Sopenharmony_ci				/* timeout; move sc to zombie list */
79462306a36Sopenharmony_ci				list_add_tail(&sc->node, &zombie_sc_list->head);
79562306a36Sopenharmony_ci				atomic_inc(&zombie_sc_list->pending_req_count);
79662306a36Sopenharmony_ci			} else {
79762306a36Sopenharmony_ci				octeon_free_soft_command(oct, sc);
79862306a36Sopenharmony_ci			}
79962306a36Sopenharmony_ci		}
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	spin_unlock_bh(sc_lists_lock);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return 0;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_free_sc_done_list);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ciint octeon_free_sc_zombie_list(struct octeon_device *oct)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	struct octeon_response_list *zombie_sc_list;
81162306a36Sopenharmony_ci	struct octeon_soft_command *sc;
81262306a36Sopenharmony_ci	struct list_head *tmp, *tmp2;
81362306a36Sopenharmony_ci	spinlock_t *sc_lists_lock; /* lock for response_list */
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	zombie_sc_list = &oct->response_list[OCTEON_ZOMBIE_SC_LIST];
81662306a36Sopenharmony_ci	sc_lists_lock = &oct->response_list[OCTEON_ORDERED_SC_LIST].lock;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	spin_lock_bh(sc_lists_lock);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	list_for_each_safe(tmp, tmp2, &zombie_sc_list->head) {
82162306a36Sopenharmony_ci		list_del(tmp);
82262306a36Sopenharmony_ci		atomic_dec(&zombie_sc_list->pending_req_count);
82362306a36Sopenharmony_ci		sc = list_entry(tmp, struct octeon_soft_command, node);
82462306a36Sopenharmony_ci		octeon_free_soft_command(oct, sc);
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	spin_unlock_bh(sc_lists_lock);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	return 0;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_free_sc_zombie_list);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ciint octeon_free_sc_buffer_pool(struct octeon_device *oct)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct list_head *tmp, *tmp2;
83662306a36Sopenharmony_ci	struct octeon_soft_command *sc;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	octeon_free_sc_zombie_list(oct);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	spin_lock_bh(&oct->sc_buf_pool.lock);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	list_for_each_safe(tmp, tmp2, &oct->sc_buf_pool.head) {
84362306a36Sopenharmony_ci		list_del(tmp);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci		sc = (struct octeon_soft_command *)tmp;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci		lio_dma_free(oct, sc->size, sc, sc->dma_addr);
84862306a36Sopenharmony_ci	}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	INIT_LIST_HEAD(&oct->sc_buf_pool.head);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	spin_unlock_bh(&oct->sc_buf_pool.lock);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	return 0;
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_free_sc_buffer_pool);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistruct octeon_soft_command *octeon_alloc_soft_command(struct octeon_device *oct,
85962306a36Sopenharmony_ci						      u32 datasize,
86062306a36Sopenharmony_ci						      u32 rdatasize,
86162306a36Sopenharmony_ci						      u32 ctxsize)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	u64 dma_addr;
86462306a36Sopenharmony_ci	u32 size;
86562306a36Sopenharmony_ci	u32 offset = sizeof(struct octeon_soft_command);
86662306a36Sopenharmony_ci	struct octeon_soft_command *sc = NULL;
86762306a36Sopenharmony_ci	struct list_head *tmp;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	if (!rdatasize)
87062306a36Sopenharmony_ci		rdatasize = 16;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	WARN_ON((offset + datasize + rdatasize + ctxsize) >
87362306a36Sopenharmony_ci	       SOFT_COMMAND_BUFFER_SIZE);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	spin_lock_bh(&oct->sc_buf_pool.lock);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (list_empty(&oct->sc_buf_pool.head)) {
87862306a36Sopenharmony_ci		spin_unlock_bh(&oct->sc_buf_pool.lock);
87962306a36Sopenharmony_ci		return NULL;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	list_for_each(tmp, &oct->sc_buf_pool.head)
88362306a36Sopenharmony_ci		break;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	list_del(tmp);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	atomic_inc(&oct->sc_buf_pool.alloc_buf_count);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	spin_unlock_bh(&oct->sc_buf_pool.lock);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	sc = (struct octeon_soft_command *)tmp;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	dma_addr = sc->dma_addr;
89462306a36Sopenharmony_ci	size = sc->size;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	memset(sc, 0, sc->size);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	sc->dma_addr = dma_addr;
89962306a36Sopenharmony_ci	sc->size = size;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (ctxsize) {
90262306a36Sopenharmony_ci		sc->ctxptr = (u8 *)sc + offset;
90362306a36Sopenharmony_ci		sc->ctxsize = ctxsize;
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/* Start data at 128 byte boundary */
90762306a36Sopenharmony_ci	offset = (offset + ctxsize + 127) & 0xffffff80;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	if (datasize) {
91062306a36Sopenharmony_ci		sc->virtdptr = (u8 *)sc + offset;
91162306a36Sopenharmony_ci		sc->dmadptr = dma_addr + offset;
91262306a36Sopenharmony_ci		sc->datasize = datasize;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	/* Start rdata at 128 byte boundary */
91662306a36Sopenharmony_ci	offset = (offset + datasize + 127) & 0xffffff80;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (rdatasize) {
91962306a36Sopenharmony_ci		WARN_ON(rdatasize < 16);
92062306a36Sopenharmony_ci		sc->virtrptr = (u8 *)sc + offset;
92162306a36Sopenharmony_ci		sc->dmarptr = dma_addr + offset;
92262306a36Sopenharmony_ci		sc->rdatasize = rdatasize;
92362306a36Sopenharmony_ci		sc->status_word = (u64 *)((u8 *)(sc->virtrptr) + rdatasize - 8);
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	return sc;
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_alloc_soft_command);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_civoid octeon_free_soft_command(struct octeon_device *oct,
93162306a36Sopenharmony_ci			      struct octeon_soft_command *sc)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	spin_lock_bh(&oct->sc_buf_pool.lock);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	list_add_tail(&sc->node, &oct->sc_buf_pool.head);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	atomic_dec(&oct->sc_buf_pool.alloc_buf_count);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	spin_unlock_bh(&oct->sc_buf_pool.lock);
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_free_soft_command);
942