18c2ecf20Sopenharmony_ci/**********************************************************************
28c2ecf20Sopenharmony_ci * Author: Cavium, Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Contact: support@cavium.com
58c2ecf20Sopenharmony_ci *          Please include "LiquidIO" in the subject.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (c) 2003-2016 Cavium, Inc.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This file is free software; you can redistribute it and/or modify
108c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License, Version 2, as
118c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This file is distributed in the hope that it will be useful, but
148c2ecf20Sopenharmony_ci * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
158c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
168c2ecf20Sopenharmony_ci * NONINFRINGEMENT.  See the GNU General Public License for more
178c2ecf20Sopenharmony_ci * details.
188c2ecf20Sopenharmony_ci **********************************************************************/
198c2ecf20Sopenharmony_ci#include <linux/pci.h>
208c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
218c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
228c2ecf20Sopenharmony_ci#include "liquidio_common.h"
238c2ecf20Sopenharmony_ci#include "octeon_droq.h"
248c2ecf20Sopenharmony_ci#include "octeon_iq.h"
258c2ecf20Sopenharmony_ci#include "response_manager.h"
268c2ecf20Sopenharmony_ci#include "octeon_device.h"
278c2ecf20Sopenharmony_ci#include "octeon_main.h"
288c2ecf20Sopenharmony_ci#include "octeon_network.h"
298c2ecf20Sopenharmony_ci#include "cn66xx_device.h"
308c2ecf20Sopenharmony_ci#include "cn23xx_pf_device.h"
318c2ecf20Sopenharmony_ci#include "cn23xx_vf_device.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistruct iq_post_status {
348c2ecf20Sopenharmony_ci	int status;
358c2ecf20Sopenharmony_ci	int index;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void check_db_timeout(struct work_struct *work);
398c2ecf20Sopenharmony_cistatic void  __check_db_timeout(struct octeon_device *oct, u64 iq_no);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void (*reqtype_free_fn[MAX_OCTEON_DEVICES][REQTYPE_LAST + 1]) (void *);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic inline int IQ_INSTR_MODE_64B(struct octeon_device *oct, int iq_no)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct octeon_instr_queue *iq =
468c2ecf20Sopenharmony_ci	    (struct octeon_instr_queue *)oct->instr_queue[iq_no];
478c2ecf20Sopenharmony_ci	return iq->iqcmd_64B;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define IQ_INSTR_MODE_32B(oct, iq_no)  (!IQ_INSTR_MODE_64B(oct, iq_no))
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* Define this to return the request status comaptible to old code */
538c2ecf20Sopenharmony_ci/*#define OCTEON_USE_OLD_REQ_STATUS*/
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* Return 0 on success, 1 on failure */
568c2ecf20Sopenharmony_ciint octeon_init_instr_queue(struct octeon_device *oct,
578c2ecf20Sopenharmony_ci			    union oct_txpciq txpciq,
588c2ecf20Sopenharmony_ci			    u32 num_descs)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct octeon_instr_queue *iq;
618c2ecf20Sopenharmony_ci	struct octeon_iq_config *conf = NULL;
628c2ecf20Sopenharmony_ci	u32 iq_no = (u32)txpciq.s.q_no;
638c2ecf20Sopenharmony_ci	u32 q_size;
648c2ecf20Sopenharmony_ci	struct cavium_wq *db_wq;
658c2ecf20Sopenharmony_ci	int numa_node = dev_to_node(&oct->pci_dev->dev);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (OCTEON_CN6XXX(oct))
688c2ecf20Sopenharmony_ci		conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn6xxx)));
698c2ecf20Sopenharmony_ci	else if (OCTEON_CN23XX_PF(oct))
708c2ecf20Sopenharmony_ci		conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn23xx_pf)));
718c2ecf20Sopenharmony_ci	else if (OCTEON_CN23XX_VF(oct))
728c2ecf20Sopenharmony_ci		conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn23xx_vf)));
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (!conf) {
758c2ecf20Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Unsupported Chip %x\n",
768c2ecf20Sopenharmony_ci			oct->chip_id);
778c2ecf20Sopenharmony_ci		return 1;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	q_size = (u32)conf->instr_type * num_descs;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	iq = oct->instr_queue[iq_no];
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	iq->oct_dev = oct;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	iq->base_addr = lio_dma_alloc(oct, q_size, &iq->base_addr_dma);
878c2ecf20Sopenharmony_ci	if (!iq->base_addr) {
888c2ecf20Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Cannot allocate memory for instr queue %d\n",
898c2ecf20Sopenharmony_ci			iq_no);
908c2ecf20Sopenharmony_ci		return 1;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	iq->max_count = num_descs;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* Initialize a list to holds requests that have been posted to Octeon
968c2ecf20Sopenharmony_ci	 * but has yet to be fetched by octeon
978c2ecf20Sopenharmony_ci	 */
988c2ecf20Sopenharmony_ci	iq->request_list = vzalloc_node(array_size(num_descs, sizeof(*iq->request_list)),
998c2ecf20Sopenharmony_ci					numa_node);
1008c2ecf20Sopenharmony_ci	if (!iq->request_list)
1018c2ecf20Sopenharmony_ci		iq->request_list = vzalloc(array_size(num_descs, sizeof(*iq->request_list)));
1028c2ecf20Sopenharmony_ci	if (!iq->request_list) {
1038c2ecf20Sopenharmony_ci		lio_dma_free(oct, q_size, iq->base_addr, iq->base_addr_dma);
1048c2ecf20Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Alloc failed for IQ[%d] nr free list\n",
1058c2ecf20Sopenharmony_ci			iq_no);
1068c2ecf20Sopenharmony_ci		return 1;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	dev_dbg(&oct->pci_dev->dev, "IQ[%d]: base: %p basedma: %pad count: %d\n",
1108c2ecf20Sopenharmony_ci		iq_no, iq->base_addr, &iq->base_addr_dma, iq->max_count);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	iq->txpciq.u64 = txpciq.u64;
1138c2ecf20Sopenharmony_ci	iq->fill_threshold = (u32)conf->db_min;
1148c2ecf20Sopenharmony_ci	iq->fill_cnt = 0;
1158c2ecf20Sopenharmony_ci	iq->host_write_index = 0;
1168c2ecf20Sopenharmony_ci	iq->octeon_read_index = 0;
1178c2ecf20Sopenharmony_ci	iq->flush_index = 0;
1188c2ecf20Sopenharmony_ci	iq->last_db_time = 0;
1198c2ecf20Sopenharmony_ci	iq->do_auto_flush = 1;
1208c2ecf20Sopenharmony_ci	iq->db_timeout = (u32)conf->db_timeout;
1218c2ecf20Sopenharmony_ci	atomic_set(&iq->instr_pending, 0);
1228c2ecf20Sopenharmony_ci	iq->pkts_processed = 0;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* Initialize the spinlock for this instruction queue */
1258c2ecf20Sopenharmony_ci	spin_lock_init(&iq->lock);
1268c2ecf20Sopenharmony_ci	if (iq_no == 0) {
1278c2ecf20Sopenharmony_ci		iq->allow_soft_cmds = true;
1288c2ecf20Sopenharmony_ci		spin_lock_init(&iq->post_lock);
1298c2ecf20Sopenharmony_ci	} else {
1308c2ecf20Sopenharmony_ci		iq->allow_soft_cmds = false;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	spin_lock_init(&iq->iq_flush_running_lock);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	oct->io_qmask.iq |= BIT_ULL(iq_no);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* Set the 32B/64B mode for each input queue */
1388c2ecf20Sopenharmony_ci	oct->io_qmask.iq64B |= ((conf->instr_type == 64) << iq_no);
1398c2ecf20Sopenharmony_ci	iq->iqcmd_64B = (conf->instr_type == 64);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	oct->fn_list.setup_iq_regs(oct, iq_no);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	oct->check_db_wq[iq_no].wq = alloc_workqueue("check_iq_db",
1448c2ecf20Sopenharmony_ci						     WQ_MEM_RECLAIM,
1458c2ecf20Sopenharmony_ci						     0);
1468c2ecf20Sopenharmony_ci	if (!oct->check_db_wq[iq_no].wq) {
1478c2ecf20Sopenharmony_ci		vfree(iq->request_list);
1488c2ecf20Sopenharmony_ci		iq->request_list = NULL;
1498c2ecf20Sopenharmony_ci		lio_dma_free(oct, q_size, iq->base_addr, iq->base_addr_dma);
1508c2ecf20Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "check db wq create failed for iq %d\n",
1518c2ecf20Sopenharmony_ci			iq_no);
1528c2ecf20Sopenharmony_ci		return 1;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	db_wq = &oct->check_db_wq[iq_no];
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&db_wq->wk.work, check_db_timeout);
1588c2ecf20Sopenharmony_ci	db_wq->wk.ctxptr = oct;
1598c2ecf20Sopenharmony_ci	db_wq->wk.ctxul = iq_no;
1608c2ecf20Sopenharmony_ci	queue_delayed_work(db_wq->wq, &db_wq->wk.work, msecs_to_jiffies(1));
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ciint octeon_delete_instr_queue(struct octeon_device *oct, u32 iq_no)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	u64 desc_size = 0, q_size;
1688c2ecf20Sopenharmony_ci	struct octeon_instr_queue *iq = oct->instr_queue[iq_no];
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&oct->check_db_wq[iq_no].wk.work);
1718c2ecf20Sopenharmony_ci	destroy_workqueue(oct->check_db_wq[iq_no].wq);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (OCTEON_CN6XXX(oct))
1748c2ecf20Sopenharmony_ci		desc_size =
1758c2ecf20Sopenharmony_ci		    CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn6xxx));
1768c2ecf20Sopenharmony_ci	else if (OCTEON_CN23XX_PF(oct))
1778c2ecf20Sopenharmony_ci		desc_size =
1788c2ecf20Sopenharmony_ci		    CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn23xx_pf));
1798c2ecf20Sopenharmony_ci	else if (OCTEON_CN23XX_VF(oct))
1808c2ecf20Sopenharmony_ci		desc_size =
1818c2ecf20Sopenharmony_ci		    CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn23xx_vf));
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	vfree(iq->request_list);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (iq->base_addr) {
1868c2ecf20Sopenharmony_ci		q_size = iq->max_count * desc_size;
1878c2ecf20Sopenharmony_ci		lio_dma_free(oct, (u32)q_size, iq->base_addr,
1888c2ecf20Sopenharmony_ci			     iq->base_addr_dma);
1898c2ecf20Sopenharmony_ci		oct->io_qmask.iq &= ~(1ULL << iq_no);
1908c2ecf20Sopenharmony_ci		vfree(oct->instr_queue[iq_no]);
1918c2ecf20Sopenharmony_ci		oct->instr_queue[iq_no] = NULL;
1928c2ecf20Sopenharmony_ci		oct->num_iqs--;
1938c2ecf20Sopenharmony_ci		return 0;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	return 1;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci/* Return 0 on success, 1 on failure */
1998c2ecf20Sopenharmony_ciint octeon_setup_iq(struct octeon_device *oct,
2008c2ecf20Sopenharmony_ci		    int ifidx,
2018c2ecf20Sopenharmony_ci		    int q_index,
2028c2ecf20Sopenharmony_ci		    union oct_txpciq txpciq,
2038c2ecf20Sopenharmony_ci		    u32 num_descs,
2048c2ecf20Sopenharmony_ci		    void *app_ctx)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	u32 iq_no = (u32)txpciq.s.q_no;
2078c2ecf20Sopenharmony_ci	int numa_node = dev_to_node(&oct->pci_dev->dev);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (oct->instr_queue[iq_no]) {
2108c2ecf20Sopenharmony_ci		dev_dbg(&oct->pci_dev->dev, "IQ is in use. Cannot create the IQ: %d again\n",
2118c2ecf20Sopenharmony_ci			iq_no);
2128c2ecf20Sopenharmony_ci		oct->instr_queue[iq_no]->txpciq.u64 = txpciq.u64;
2138c2ecf20Sopenharmony_ci		oct->instr_queue[iq_no]->app_ctx = app_ctx;
2148c2ecf20Sopenharmony_ci		return 0;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci	oct->instr_queue[iq_no] =
2178c2ecf20Sopenharmony_ci	    vzalloc_node(sizeof(struct octeon_instr_queue), numa_node);
2188c2ecf20Sopenharmony_ci	if (!oct->instr_queue[iq_no])
2198c2ecf20Sopenharmony_ci		oct->instr_queue[iq_no] =
2208c2ecf20Sopenharmony_ci		    vzalloc(sizeof(struct octeon_instr_queue));
2218c2ecf20Sopenharmony_ci	if (!oct->instr_queue[iq_no])
2228c2ecf20Sopenharmony_ci		return 1;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	oct->instr_queue[iq_no]->q_index = q_index;
2268c2ecf20Sopenharmony_ci	oct->instr_queue[iq_no]->app_ctx = app_ctx;
2278c2ecf20Sopenharmony_ci	oct->instr_queue[iq_no]->ifidx = ifidx;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (octeon_init_instr_queue(oct, txpciq, num_descs)) {
2308c2ecf20Sopenharmony_ci		vfree(oct->instr_queue[iq_no]);
2318c2ecf20Sopenharmony_ci		oct->instr_queue[iq_no] = NULL;
2328c2ecf20Sopenharmony_ci		return 1;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	oct->num_iqs++;
2368c2ecf20Sopenharmony_ci	if (oct->fn_list.enable_io_queues(oct)) {
2378c2ecf20Sopenharmony_ci		octeon_delete_instr_queue(oct, iq_no);
2388c2ecf20Sopenharmony_ci		return 1;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return 0;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ciint lio_wait_for_instr_fetch(struct octeon_device *oct)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	int i, retry = 1000, pending, instr_cnt = 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	do {
2498c2ecf20Sopenharmony_ci		instr_cnt = 0;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) {
2528c2ecf20Sopenharmony_ci			if (!(oct->io_qmask.iq & BIT_ULL(i)))
2538c2ecf20Sopenharmony_ci				continue;
2548c2ecf20Sopenharmony_ci			pending =
2558c2ecf20Sopenharmony_ci			    atomic_read(&oct->instr_queue[i]->instr_pending);
2568c2ecf20Sopenharmony_ci			if (pending)
2578c2ecf20Sopenharmony_ci				__check_db_timeout(oct, i);
2588c2ecf20Sopenharmony_ci			instr_cnt += pending;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		if (instr_cnt == 0)
2628c2ecf20Sopenharmony_ci			break;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci		schedule_timeout_uninterruptible(1);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	} while (retry-- && instr_cnt);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	return instr_cnt;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic inline void
2728c2ecf20Sopenharmony_ciring_doorbell(struct octeon_device *oct, struct octeon_instr_queue *iq)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	if (atomic_read(&oct->status) == OCT_DEV_RUNNING) {
2758c2ecf20Sopenharmony_ci		writel(iq->fill_cnt, iq->doorbell_reg);
2768c2ecf20Sopenharmony_ci		/* make sure doorbell write goes through */
2778c2ecf20Sopenharmony_ci		iq->fill_cnt = 0;
2788c2ecf20Sopenharmony_ci		iq->last_db_time = jiffies;
2798c2ecf20Sopenharmony_ci		return;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_civoid
2848c2ecf20Sopenharmony_ciocteon_ring_doorbell_locked(struct octeon_device *oct, u32 iq_no)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct octeon_instr_queue *iq;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	iq = oct->instr_queue[iq_no];
2898c2ecf20Sopenharmony_ci	spin_lock(&iq->post_lock);
2908c2ecf20Sopenharmony_ci	if (iq->fill_cnt)
2918c2ecf20Sopenharmony_ci		ring_doorbell(oct, iq);
2928c2ecf20Sopenharmony_ci	spin_unlock(&iq->post_lock);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic inline void __copy_cmd_into_iq(struct octeon_instr_queue *iq,
2968c2ecf20Sopenharmony_ci				      u8 *cmd)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	u8 *iqptr, cmdsize;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	cmdsize = ((iq->iqcmd_64B) ? 64 : 32);
3018c2ecf20Sopenharmony_ci	iqptr = iq->base_addr + (cmdsize * iq->host_write_index);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	memcpy(iqptr, cmd, cmdsize);
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic inline struct iq_post_status
3078c2ecf20Sopenharmony_ci__post_command2(struct octeon_instr_queue *iq, u8 *cmd)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct iq_post_status st;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	st.status = IQ_SEND_OK;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* This ensures that the read index does not wrap around to the same
3148c2ecf20Sopenharmony_ci	 * position if queue gets full before Octeon could fetch any instr.
3158c2ecf20Sopenharmony_ci	 */
3168c2ecf20Sopenharmony_ci	if (atomic_read(&iq->instr_pending) >= (s32)(iq->max_count - 1)) {
3178c2ecf20Sopenharmony_ci		st.status = IQ_SEND_FAILED;
3188c2ecf20Sopenharmony_ci		st.index = -1;
3198c2ecf20Sopenharmony_ci		return st;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (atomic_read(&iq->instr_pending) >= (s32)(iq->max_count - 2))
3238c2ecf20Sopenharmony_ci		st.status = IQ_SEND_STOP;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	__copy_cmd_into_iq(iq, cmd);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* "index" is returned, host_write_index is modified. */
3288c2ecf20Sopenharmony_ci	st.index = iq->host_write_index;
3298c2ecf20Sopenharmony_ci	iq->host_write_index = incr_index(iq->host_write_index, 1,
3308c2ecf20Sopenharmony_ci					  iq->max_count);
3318c2ecf20Sopenharmony_ci	iq->fill_cnt++;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/* Flush the command into memory. We need to be sure the data is in
3348c2ecf20Sopenharmony_ci	 * memory before indicating that the instruction is pending.
3358c2ecf20Sopenharmony_ci	 */
3368c2ecf20Sopenharmony_ci	wmb();
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	atomic_inc(&iq->instr_pending);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	return st;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ciint
3448c2ecf20Sopenharmony_ciocteon_register_reqtype_free_fn(struct octeon_device *oct, int reqtype,
3458c2ecf20Sopenharmony_ci				void (*fn)(void *))
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	if (reqtype > REQTYPE_LAST) {
3488c2ecf20Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "%s: Invalid reqtype: %d\n",
3498c2ecf20Sopenharmony_ci			__func__, reqtype);
3508c2ecf20Sopenharmony_ci		return -EINVAL;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	reqtype_free_fn[oct->octeon_id][reqtype] = fn;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	return 0;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic inline void
3598c2ecf20Sopenharmony_ci__add_to_request_list(struct octeon_instr_queue *iq,
3608c2ecf20Sopenharmony_ci		      int idx, void *buf, int reqtype)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	iq->request_list[idx].buf = buf;
3638c2ecf20Sopenharmony_ci	iq->request_list[idx].reqtype = reqtype;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci/* Can only run in process context */
3678c2ecf20Sopenharmony_ciint
3688c2ecf20Sopenharmony_cilio_process_iq_request_list(struct octeon_device *oct,
3698c2ecf20Sopenharmony_ci			    struct octeon_instr_queue *iq, u32 napi_budget)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct cavium_wq *cwq = &oct->dma_comp_wq;
3728c2ecf20Sopenharmony_ci	int reqtype;
3738c2ecf20Sopenharmony_ci	void *buf;
3748c2ecf20Sopenharmony_ci	u32 old = iq->flush_index;
3758c2ecf20Sopenharmony_ci	u32 inst_count = 0;
3768c2ecf20Sopenharmony_ci	unsigned int pkts_compl = 0, bytes_compl = 0;
3778c2ecf20Sopenharmony_ci	struct octeon_soft_command *sc;
3788c2ecf20Sopenharmony_ci	unsigned long flags;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	while (old != iq->octeon_read_index) {
3818c2ecf20Sopenharmony_ci		reqtype = iq->request_list[old].reqtype;
3828c2ecf20Sopenharmony_ci		buf     = iq->request_list[old].buf;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		if (reqtype == REQTYPE_NONE)
3858c2ecf20Sopenharmony_ci			goto skip_this;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		octeon_update_tx_completion_counters(buf, reqtype, &pkts_compl,
3888c2ecf20Sopenharmony_ci						     &bytes_compl);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci		switch (reqtype) {
3918c2ecf20Sopenharmony_ci		case REQTYPE_NORESP_NET:
3928c2ecf20Sopenharmony_ci		case REQTYPE_NORESP_NET_SG:
3938c2ecf20Sopenharmony_ci		case REQTYPE_RESP_NET_SG:
3948c2ecf20Sopenharmony_ci			reqtype_free_fn[oct->octeon_id][reqtype](buf);
3958c2ecf20Sopenharmony_ci			break;
3968c2ecf20Sopenharmony_ci		case REQTYPE_RESP_NET:
3978c2ecf20Sopenharmony_ci		case REQTYPE_SOFT_COMMAND:
3988c2ecf20Sopenharmony_ci			sc = buf;
3998c2ecf20Sopenharmony_ci			/* We're expecting a response from Octeon.
4008c2ecf20Sopenharmony_ci			 * It's up to lio_process_ordered_list() to
4018c2ecf20Sopenharmony_ci			 * process  sc. Add sc to the ordered soft
4028c2ecf20Sopenharmony_ci			 * command response list because we expect
4038c2ecf20Sopenharmony_ci			 * a response from Octeon.
4048c2ecf20Sopenharmony_ci			 */
4058c2ecf20Sopenharmony_ci			spin_lock_irqsave(&oct->response_list
4068c2ecf20Sopenharmony_ci					  [OCTEON_ORDERED_SC_LIST].lock, flags);
4078c2ecf20Sopenharmony_ci			atomic_inc(&oct->response_list
4088c2ecf20Sopenharmony_ci				   [OCTEON_ORDERED_SC_LIST].pending_req_count);
4098c2ecf20Sopenharmony_ci			list_add_tail(&sc->node, &oct->response_list
4108c2ecf20Sopenharmony_ci				[OCTEON_ORDERED_SC_LIST].head);
4118c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&oct->response_list
4128c2ecf20Sopenharmony_ci					       [OCTEON_ORDERED_SC_LIST].lock,
4138c2ecf20Sopenharmony_ci					       flags);
4148c2ecf20Sopenharmony_ci			break;
4158c2ecf20Sopenharmony_ci		default:
4168c2ecf20Sopenharmony_ci			dev_err(&oct->pci_dev->dev,
4178c2ecf20Sopenharmony_ci				"%s Unknown reqtype: %d buf: %p at idx %d\n",
4188c2ecf20Sopenharmony_ci				__func__, reqtype, buf, old);
4198c2ecf20Sopenharmony_ci		}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		iq->request_list[old].buf = NULL;
4228c2ecf20Sopenharmony_ci		iq->request_list[old].reqtype = 0;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci skip_this:
4258c2ecf20Sopenharmony_ci		inst_count++;
4268c2ecf20Sopenharmony_ci		old = incr_index(old, 1, iq->max_count);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci		if ((napi_budget) && (inst_count >= napi_budget))
4298c2ecf20Sopenharmony_ci			break;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci	if (bytes_compl)
4328c2ecf20Sopenharmony_ci		octeon_report_tx_completion_to_bql(iq->app_ctx, pkts_compl,
4338c2ecf20Sopenharmony_ci						   bytes_compl);
4348c2ecf20Sopenharmony_ci	iq->flush_index = old;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if (atomic_read(&oct->response_list
4378c2ecf20Sopenharmony_ci			[OCTEON_ORDERED_SC_LIST].pending_req_count))
4388c2ecf20Sopenharmony_ci		queue_work(cwq->wq, &cwq->wk.work.work);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	return inst_count;
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci/* Can only be called from process context */
4448c2ecf20Sopenharmony_ciint
4458c2ecf20Sopenharmony_ciocteon_flush_iq(struct octeon_device *oct, struct octeon_instr_queue *iq,
4468c2ecf20Sopenharmony_ci		u32 napi_budget)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	u32 inst_processed = 0;
4498c2ecf20Sopenharmony_ci	u32 tot_inst_processed = 0;
4508c2ecf20Sopenharmony_ci	int tx_done = 1;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (!spin_trylock(&iq->iq_flush_running_lock))
4538c2ecf20Sopenharmony_ci		return tx_done;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	spin_lock_bh(&iq->lock);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	iq->octeon_read_index = oct->fn_list.update_iq_read_idx(iq);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	do {
4608c2ecf20Sopenharmony_ci		/* Process any outstanding IQ packets. */
4618c2ecf20Sopenharmony_ci		if (iq->flush_index == iq->octeon_read_index)
4628c2ecf20Sopenharmony_ci			break;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		if (napi_budget)
4658c2ecf20Sopenharmony_ci			inst_processed =
4668c2ecf20Sopenharmony_ci				lio_process_iq_request_list(oct, iq,
4678c2ecf20Sopenharmony_ci							    napi_budget -
4688c2ecf20Sopenharmony_ci							    tot_inst_processed);
4698c2ecf20Sopenharmony_ci		else
4708c2ecf20Sopenharmony_ci			inst_processed =
4718c2ecf20Sopenharmony_ci				lio_process_iq_request_list(oct, iq, 0);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		if (inst_processed) {
4748c2ecf20Sopenharmony_ci			iq->pkts_processed += inst_processed;
4758c2ecf20Sopenharmony_ci			atomic_sub(inst_processed, &iq->instr_pending);
4768c2ecf20Sopenharmony_ci			iq->stats.instr_processed += inst_processed;
4778c2ecf20Sopenharmony_ci		}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci		tot_inst_processed += inst_processed;
4808c2ecf20Sopenharmony_ci	} while (tot_inst_processed < napi_budget);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (napi_budget && (tot_inst_processed >= napi_budget))
4838c2ecf20Sopenharmony_ci		tx_done = 0;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	iq->last_db_time = jiffies;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	spin_unlock_bh(&iq->lock);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	spin_unlock(&iq->iq_flush_running_lock);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	return tx_done;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci/* Process instruction queue after timeout.
4958c2ecf20Sopenharmony_ci * This routine gets called from a workqueue or when removing the module.
4968c2ecf20Sopenharmony_ci */
4978c2ecf20Sopenharmony_cistatic void __check_db_timeout(struct octeon_device *oct, u64 iq_no)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	struct octeon_instr_queue *iq;
5008c2ecf20Sopenharmony_ci	u64 next_time;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (!oct)
5038c2ecf20Sopenharmony_ci		return;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	iq = oct->instr_queue[iq_no];
5068c2ecf20Sopenharmony_ci	if (!iq)
5078c2ecf20Sopenharmony_ci		return;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	/* return immediately, if no work pending */
5108c2ecf20Sopenharmony_ci	if (!atomic_read(&iq->instr_pending))
5118c2ecf20Sopenharmony_ci		return;
5128c2ecf20Sopenharmony_ci	/* If jiffies - last_db_time < db_timeout do nothing  */
5138c2ecf20Sopenharmony_ci	next_time = iq->last_db_time + iq->db_timeout;
5148c2ecf20Sopenharmony_ci	if (!time_after(jiffies, (unsigned long)next_time))
5158c2ecf20Sopenharmony_ci		return;
5168c2ecf20Sopenharmony_ci	iq->last_db_time = jiffies;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	/* Flush the instruction queue */
5198c2ecf20Sopenharmony_ci	octeon_flush_iq(oct, iq, 0);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	lio_enable_irq(NULL, iq);
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci/* Called by the Poll thread at regular intervals to check the instruction
5258c2ecf20Sopenharmony_ci * queue for commands to be posted and for commands that were fetched by Octeon.
5268c2ecf20Sopenharmony_ci */
5278c2ecf20Sopenharmony_cistatic void check_db_timeout(struct work_struct *work)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	struct cavium_wk *wk = (struct cavium_wk *)work;
5308c2ecf20Sopenharmony_ci	struct octeon_device *oct = (struct octeon_device *)wk->ctxptr;
5318c2ecf20Sopenharmony_ci	u64 iq_no = wk->ctxul;
5328c2ecf20Sopenharmony_ci	struct cavium_wq *db_wq = &oct->check_db_wq[iq_no];
5338c2ecf20Sopenharmony_ci	u32 delay = 10;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	__check_db_timeout(oct, iq_no);
5368c2ecf20Sopenharmony_ci	queue_delayed_work(db_wq->wq, &db_wq->wk.work, msecs_to_jiffies(delay));
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ciint
5408c2ecf20Sopenharmony_ciocteon_send_command(struct octeon_device *oct, u32 iq_no,
5418c2ecf20Sopenharmony_ci		    u32 force_db, void *cmd, void *buf,
5428c2ecf20Sopenharmony_ci		    u32 datasize, u32 reqtype)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	int xmit_stopped;
5458c2ecf20Sopenharmony_ci	struct iq_post_status st;
5468c2ecf20Sopenharmony_ci	struct octeon_instr_queue *iq = oct->instr_queue[iq_no];
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* Get the lock and prevent other tasks and tx interrupt handler from
5498c2ecf20Sopenharmony_ci	 * running.
5508c2ecf20Sopenharmony_ci	 */
5518c2ecf20Sopenharmony_ci	if (iq->allow_soft_cmds)
5528c2ecf20Sopenharmony_ci		spin_lock_bh(&iq->post_lock);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	st = __post_command2(iq, cmd);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	if (st.status != IQ_SEND_FAILED) {
5578c2ecf20Sopenharmony_ci		xmit_stopped = octeon_report_sent_bytes_to_bql(buf, reqtype);
5588c2ecf20Sopenharmony_ci		__add_to_request_list(iq, st.index, buf, reqtype);
5598c2ecf20Sopenharmony_ci		INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, bytes_sent, datasize);
5608c2ecf20Sopenharmony_ci		INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, instr_posted, 1);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci		if (iq->fill_cnt >= MAX_OCTEON_FILL_COUNT || force_db ||
5638c2ecf20Sopenharmony_ci		    xmit_stopped || st.status == IQ_SEND_STOP)
5648c2ecf20Sopenharmony_ci			ring_doorbell(oct, iq);
5658c2ecf20Sopenharmony_ci	} else {
5668c2ecf20Sopenharmony_ci		INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, instr_dropped, 1);
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	if (iq->allow_soft_cmds)
5708c2ecf20Sopenharmony_ci		spin_unlock_bh(&iq->post_lock);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* This is only done here to expedite packets being flushed
5738c2ecf20Sopenharmony_ci	 * for cases where there are no IQ completion interrupts.
5748c2ecf20Sopenharmony_ci	 */
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	return st.status;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_civoid
5808c2ecf20Sopenharmony_ciocteon_prepare_soft_command(struct octeon_device *oct,
5818c2ecf20Sopenharmony_ci			    struct octeon_soft_command *sc,
5828c2ecf20Sopenharmony_ci			    u8 opcode,
5838c2ecf20Sopenharmony_ci			    u8 subcode,
5848c2ecf20Sopenharmony_ci			    u32 irh_ossp,
5858c2ecf20Sopenharmony_ci			    u64 ossp0,
5868c2ecf20Sopenharmony_ci			    u64 ossp1)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	struct octeon_config *oct_cfg;
5898c2ecf20Sopenharmony_ci	struct octeon_instr_ih2 *ih2;
5908c2ecf20Sopenharmony_ci	struct octeon_instr_ih3 *ih3;
5918c2ecf20Sopenharmony_ci	struct octeon_instr_pki_ih3 *pki_ih3;
5928c2ecf20Sopenharmony_ci	struct octeon_instr_irh *irh;
5938c2ecf20Sopenharmony_ci	struct octeon_instr_rdp *rdp;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	WARN_ON(opcode > 15);
5968c2ecf20Sopenharmony_ci	WARN_ON(subcode > 127);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	oct_cfg = octeon_get_conf(oct);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) {
6018c2ecf20Sopenharmony_ci		ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci		ih3->pkind = oct->instr_queue[sc->iq_no]->txpciq.s.pkind;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci		pki_ih3 = (struct octeon_instr_pki_ih3 *)&sc->cmd.cmd3.pki_ih3;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci		pki_ih3->w           = 1;
6088c2ecf20Sopenharmony_ci		pki_ih3->raw         = 1;
6098c2ecf20Sopenharmony_ci		pki_ih3->utag        = 1;
6108c2ecf20Sopenharmony_ci		pki_ih3->uqpg        =
6118c2ecf20Sopenharmony_ci			oct->instr_queue[sc->iq_no]->txpciq.s.use_qpg;
6128c2ecf20Sopenharmony_ci		pki_ih3->utt         = 1;
6138c2ecf20Sopenharmony_ci		pki_ih3->tag     = LIO_CONTROL;
6148c2ecf20Sopenharmony_ci		pki_ih3->tagtype = ATOMIC_TAG;
6158c2ecf20Sopenharmony_ci		pki_ih3->qpg         =
6168c2ecf20Sopenharmony_ci			oct->instr_queue[sc->iq_no]->txpciq.s.ctrl_qpg;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci		pki_ih3->pm          = 0x7;
6198c2ecf20Sopenharmony_ci		pki_ih3->sl          = 8;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci		if (sc->datasize)
6228c2ecf20Sopenharmony_ci			ih3->dlengsz = sc->datasize;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci		irh            = (struct octeon_instr_irh *)&sc->cmd.cmd3.irh;
6258c2ecf20Sopenharmony_ci		irh->opcode    = opcode;
6268c2ecf20Sopenharmony_ci		irh->subcode   = subcode;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci		/* opcode/subcode specific parameters (ossp) */
6298c2ecf20Sopenharmony_ci		irh->ossp       = irh_ossp;
6308c2ecf20Sopenharmony_ci		sc->cmd.cmd3.ossp[0] = ossp0;
6318c2ecf20Sopenharmony_ci		sc->cmd.cmd3.ossp[1] = ossp1;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci		if (sc->rdatasize) {
6348c2ecf20Sopenharmony_ci			rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd3.rdp;
6358c2ecf20Sopenharmony_ci			rdp->pcie_port = oct->pcie_port;
6368c2ecf20Sopenharmony_ci			rdp->rlen      = sc->rdatasize;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci			irh->rflag =  1;
6398c2ecf20Sopenharmony_ci			/*PKI IH3*/
6408c2ecf20Sopenharmony_ci			/* pki_ih3 irh+ossp[0]+ossp[1]+rdp+rptr = 48 bytes */
6418c2ecf20Sopenharmony_ci			ih3->fsz    = LIO_SOFTCMDRESP_IH3;
6428c2ecf20Sopenharmony_ci		} else {
6438c2ecf20Sopenharmony_ci			irh->rflag =  0;
6448c2ecf20Sopenharmony_ci			/*PKI IH3*/
6458c2ecf20Sopenharmony_ci			/* pki_h3 + irh + ossp[0] + ossp[1] = 32 bytes */
6468c2ecf20Sopenharmony_ci			ih3->fsz    = LIO_PCICMD_O3;
6478c2ecf20Sopenharmony_ci		}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	} else {
6508c2ecf20Sopenharmony_ci		ih2          = (struct octeon_instr_ih2 *)&sc->cmd.cmd2.ih2;
6518c2ecf20Sopenharmony_ci		ih2->tagtype = ATOMIC_TAG;
6528c2ecf20Sopenharmony_ci		ih2->tag     = LIO_CONTROL;
6538c2ecf20Sopenharmony_ci		ih2->raw     = 1;
6548c2ecf20Sopenharmony_ci		ih2->grp     = CFG_GET_CTRL_Q_GRP(oct_cfg);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		if (sc->datasize) {
6578c2ecf20Sopenharmony_ci			ih2->dlengsz = sc->datasize;
6588c2ecf20Sopenharmony_ci			ih2->rs = 1;
6598c2ecf20Sopenharmony_ci		}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci		irh            = (struct octeon_instr_irh *)&sc->cmd.cmd2.irh;
6628c2ecf20Sopenharmony_ci		irh->opcode    = opcode;
6638c2ecf20Sopenharmony_ci		irh->subcode   = subcode;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci		/* opcode/subcode specific parameters (ossp) */
6668c2ecf20Sopenharmony_ci		irh->ossp       = irh_ossp;
6678c2ecf20Sopenharmony_ci		sc->cmd.cmd2.ossp[0] = ossp0;
6688c2ecf20Sopenharmony_ci		sc->cmd.cmd2.ossp[1] = ossp1;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci		if (sc->rdatasize) {
6718c2ecf20Sopenharmony_ci			rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd2.rdp;
6728c2ecf20Sopenharmony_ci			rdp->pcie_port = oct->pcie_port;
6738c2ecf20Sopenharmony_ci			rdp->rlen      = sc->rdatasize;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci			irh->rflag =  1;
6768c2ecf20Sopenharmony_ci			/* irh+ossp[0]+ossp[1]+rdp+rptr = 40 bytes */
6778c2ecf20Sopenharmony_ci			ih2->fsz   = LIO_SOFTCMDRESP_IH2;
6788c2ecf20Sopenharmony_ci		} else {
6798c2ecf20Sopenharmony_ci			irh->rflag =  0;
6808c2ecf20Sopenharmony_ci			/* irh + ossp[0] + ossp[1] = 24 bytes */
6818c2ecf20Sopenharmony_ci			ih2->fsz   = LIO_PCICMD_O2;
6828c2ecf20Sopenharmony_ci		}
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ciint octeon_send_soft_command(struct octeon_device *oct,
6878c2ecf20Sopenharmony_ci			     struct octeon_soft_command *sc)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	struct octeon_instr_queue *iq;
6908c2ecf20Sopenharmony_ci	struct octeon_instr_ih2 *ih2;
6918c2ecf20Sopenharmony_ci	struct octeon_instr_ih3 *ih3;
6928c2ecf20Sopenharmony_ci	struct octeon_instr_irh *irh;
6938c2ecf20Sopenharmony_ci	u32 len;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	iq = oct->instr_queue[sc->iq_no];
6968c2ecf20Sopenharmony_ci	if (!iq->allow_soft_cmds) {
6978c2ecf20Sopenharmony_ci		dev_err(&oct->pci_dev->dev, "Soft commands are not allowed on Queue %d\n",
6988c2ecf20Sopenharmony_ci			sc->iq_no);
6998c2ecf20Sopenharmony_ci		INCR_INSTRQUEUE_PKT_COUNT(oct, sc->iq_no, instr_dropped, 1);
7008c2ecf20Sopenharmony_ci		return IQ_SEND_FAILED;
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) {
7048c2ecf20Sopenharmony_ci		ih3 =  (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3;
7058c2ecf20Sopenharmony_ci		if (ih3->dlengsz) {
7068c2ecf20Sopenharmony_ci			WARN_ON(!sc->dmadptr);
7078c2ecf20Sopenharmony_ci			sc->cmd.cmd3.dptr = sc->dmadptr;
7088c2ecf20Sopenharmony_ci		}
7098c2ecf20Sopenharmony_ci		irh = (struct octeon_instr_irh *)&sc->cmd.cmd3.irh;
7108c2ecf20Sopenharmony_ci		if (irh->rflag) {
7118c2ecf20Sopenharmony_ci			WARN_ON(!sc->dmarptr);
7128c2ecf20Sopenharmony_ci			WARN_ON(!sc->status_word);
7138c2ecf20Sopenharmony_ci			*sc->status_word = COMPLETION_WORD_INIT;
7148c2ecf20Sopenharmony_ci			sc->cmd.cmd3.rptr = sc->dmarptr;
7158c2ecf20Sopenharmony_ci		}
7168c2ecf20Sopenharmony_ci		len = (u32)ih3->dlengsz;
7178c2ecf20Sopenharmony_ci	} else {
7188c2ecf20Sopenharmony_ci		ih2 = (struct octeon_instr_ih2 *)&sc->cmd.cmd2.ih2;
7198c2ecf20Sopenharmony_ci		if (ih2->dlengsz) {
7208c2ecf20Sopenharmony_ci			WARN_ON(!sc->dmadptr);
7218c2ecf20Sopenharmony_ci			sc->cmd.cmd2.dptr = sc->dmadptr;
7228c2ecf20Sopenharmony_ci		}
7238c2ecf20Sopenharmony_ci		irh = (struct octeon_instr_irh *)&sc->cmd.cmd2.irh;
7248c2ecf20Sopenharmony_ci		if (irh->rflag) {
7258c2ecf20Sopenharmony_ci			WARN_ON(!sc->dmarptr);
7268c2ecf20Sopenharmony_ci			WARN_ON(!sc->status_word);
7278c2ecf20Sopenharmony_ci			*sc->status_word = COMPLETION_WORD_INIT;
7288c2ecf20Sopenharmony_ci			sc->cmd.cmd2.rptr = sc->dmarptr;
7298c2ecf20Sopenharmony_ci		}
7308c2ecf20Sopenharmony_ci		len = (u32)ih2->dlengsz;
7318c2ecf20Sopenharmony_ci	}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	sc->expiry_time = jiffies + msecs_to_jiffies(LIO_SC_MAX_TMO_MS);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	return (octeon_send_command(oct, sc->iq_no, 1, &sc->cmd, sc,
7368c2ecf20Sopenharmony_ci				    len, REQTYPE_SOFT_COMMAND));
7378c2ecf20Sopenharmony_ci}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ciint octeon_setup_sc_buffer_pool(struct octeon_device *oct)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	int i;
7428c2ecf20Sopenharmony_ci	u64 dma_addr;
7438c2ecf20Sopenharmony_ci	struct octeon_soft_command *sc;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&oct->sc_buf_pool.head);
7468c2ecf20Sopenharmony_ci	spin_lock_init(&oct->sc_buf_pool.lock);
7478c2ecf20Sopenharmony_ci	atomic_set(&oct->sc_buf_pool.alloc_buf_count, 0);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_SOFT_COMMAND_BUFFERS; i++) {
7508c2ecf20Sopenharmony_ci		sc = (struct octeon_soft_command *)
7518c2ecf20Sopenharmony_ci			lio_dma_alloc(oct,
7528c2ecf20Sopenharmony_ci				      SOFT_COMMAND_BUFFER_SIZE,
7538c2ecf20Sopenharmony_ci					  (dma_addr_t *)&dma_addr);
7548c2ecf20Sopenharmony_ci		if (!sc) {
7558c2ecf20Sopenharmony_ci			octeon_free_sc_buffer_pool(oct);
7568c2ecf20Sopenharmony_ci			return 1;
7578c2ecf20Sopenharmony_ci		}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci		sc->dma_addr = dma_addr;
7608c2ecf20Sopenharmony_ci		sc->size = SOFT_COMMAND_BUFFER_SIZE;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci		list_add_tail(&sc->node, &oct->sc_buf_pool.head);
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	return 0;
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ciint octeon_free_sc_done_list(struct octeon_device *oct)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	struct octeon_response_list *done_sc_list, *zombie_sc_list;
7718c2ecf20Sopenharmony_ci	struct octeon_soft_command *sc;
7728c2ecf20Sopenharmony_ci	struct list_head *tmp, *tmp2;
7738c2ecf20Sopenharmony_ci	spinlock_t *sc_lists_lock; /* lock for response_list */
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	done_sc_list = &oct->response_list[OCTEON_DONE_SC_LIST];
7768c2ecf20Sopenharmony_ci	zombie_sc_list = &oct->response_list[OCTEON_ZOMBIE_SC_LIST];
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	if (!atomic_read(&done_sc_list->pending_req_count))
7798c2ecf20Sopenharmony_ci		return 0;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	sc_lists_lock = &oct->response_list[OCTEON_ORDERED_SC_LIST].lock;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	spin_lock_bh(sc_lists_lock);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	list_for_each_safe(tmp, tmp2, &done_sc_list->head) {
7868c2ecf20Sopenharmony_ci		sc = list_entry(tmp, struct octeon_soft_command, node);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci		if (READ_ONCE(sc->caller_is_done)) {
7898c2ecf20Sopenharmony_ci			list_del(&sc->node);
7908c2ecf20Sopenharmony_ci			atomic_dec(&done_sc_list->pending_req_count);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci			if (*sc->status_word == COMPLETION_WORD_INIT) {
7938c2ecf20Sopenharmony_ci				/* timeout; move sc to zombie list */
7948c2ecf20Sopenharmony_ci				list_add_tail(&sc->node, &zombie_sc_list->head);
7958c2ecf20Sopenharmony_ci				atomic_inc(&zombie_sc_list->pending_req_count);
7968c2ecf20Sopenharmony_ci			} else {
7978c2ecf20Sopenharmony_ci				octeon_free_soft_command(oct, sc);
7988c2ecf20Sopenharmony_ci			}
7998c2ecf20Sopenharmony_ci		}
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	spin_unlock_bh(sc_lists_lock);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	return 0;
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ciint octeon_free_sc_zombie_list(struct octeon_device *oct)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	struct octeon_response_list *zombie_sc_list;
8108c2ecf20Sopenharmony_ci	struct octeon_soft_command *sc;
8118c2ecf20Sopenharmony_ci	struct list_head *tmp, *tmp2;
8128c2ecf20Sopenharmony_ci	spinlock_t *sc_lists_lock; /* lock for response_list */
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	zombie_sc_list = &oct->response_list[OCTEON_ZOMBIE_SC_LIST];
8158c2ecf20Sopenharmony_ci	sc_lists_lock = &oct->response_list[OCTEON_ORDERED_SC_LIST].lock;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	spin_lock_bh(sc_lists_lock);
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	list_for_each_safe(tmp, tmp2, &zombie_sc_list->head) {
8208c2ecf20Sopenharmony_ci		list_del(tmp);
8218c2ecf20Sopenharmony_ci		atomic_dec(&zombie_sc_list->pending_req_count);
8228c2ecf20Sopenharmony_ci		sc = list_entry(tmp, struct octeon_soft_command, node);
8238c2ecf20Sopenharmony_ci		octeon_free_soft_command(oct, sc);
8248c2ecf20Sopenharmony_ci	}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	spin_unlock_bh(sc_lists_lock);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	return 0;
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ciint octeon_free_sc_buffer_pool(struct octeon_device *oct)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	struct list_head *tmp, *tmp2;
8348c2ecf20Sopenharmony_ci	struct octeon_soft_command *sc;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	octeon_free_sc_zombie_list(oct);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	spin_lock_bh(&oct->sc_buf_pool.lock);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	list_for_each_safe(tmp, tmp2, &oct->sc_buf_pool.head) {
8418c2ecf20Sopenharmony_ci		list_del(tmp);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci		sc = (struct octeon_soft_command *)tmp;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci		lio_dma_free(oct, sc->size, sc, sc->dma_addr);
8468c2ecf20Sopenharmony_ci	}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&oct->sc_buf_pool.head);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	spin_unlock_bh(&oct->sc_buf_pool.lock);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	return 0;
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_cistruct octeon_soft_command *octeon_alloc_soft_command(struct octeon_device *oct,
8568c2ecf20Sopenharmony_ci						      u32 datasize,
8578c2ecf20Sopenharmony_ci						      u32 rdatasize,
8588c2ecf20Sopenharmony_ci						      u32 ctxsize)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci	u64 dma_addr;
8618c2ecf20Sopenharmony_ci	u32 size;
8628c2ecf20Sopenharmony_ci	u32 offset = sizeof(struct octeon_soft_command);
8638c2ecf20Sopenharmony_ci	struct octeon_soft_command *sc = NULL;
8648c2ecf20Sopenharmony_ci	struct list_head *tmp;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	if (!rdatasize)
8678c2ecf20Sopenharmony_ci		rdatasize = 16;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	WARN_ON((offset + datasize + rdatasize + ctxsize) >
8708c2ecf20Sopenharmony_ci	       SOFT_COMMAND_BUFFER_SIZE);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	spin_lock_bh(&oct->sc_buf_pool.lock);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if (list_empty(&oct->sc_buf_pool.head)) {
8758c2ecf20Sopenharmony_ci		spin_unlock_bh(&oct->sc_buf_pool.lock);
8768c2ecf20Sopenharmony_ci		return NULL;
8778c2ecf20Sopenharmony_ci	}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	list_for_each(tmp, &oct->sc_buf_pool.head)
8808c2ecf20Sopenharmony_ci		break;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	list_del(tmp);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	atomic_inc(&oct->sc_buf_pool.alloc_buf_count);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	spin_unlock_bh(&oct->sc_buf_pool.lock);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	sc = (struct octeon_soft_command *)tmp;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	dma_addr = sc->dma_addr;
8918c2ecf20Sopenharmony_ci	size = sc->size;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	memset(sc, 0, sc->size);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	sc->dma_addr = dma_addr;
8968c2ecf20Sopenharmony_ci	sc->size = size;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	if (ctxsize) {
8998c2ecf20Sopenharmony_ci		sc->ctxptr = (u8 *)sc + offset;
9008c2ecf20Sopenharmony_ci		sc->ctxsize = ctxsize;
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	/* Start data at 128 byte boundary */
9048c2ecf20Sopenharmony_ci	offset = (offset + ctxsize + 127) & 0xffffff80;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	if (datasize) {
9078c2ecf20Sopenharmony_ci		sc->virtdptr = (u8 *)sc + offset;
9088c2ecf20Sopenharmony_ci		sc->dmadptr = dma_addr + offset;
9098c2ecf20Sopenharmony_ci		sc->datasize = datasize;
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	/* Start rdata at 128 byte boundary */
9138c2ecf20Sopenharmony_ci	offset = (offset + datasize + 127) & 0xffffff80;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	if (rdatasize) {
9168c2ecf20Sopenharmony_ci		WARN_ON(rdatasize < 16);
9178c2ecf20Sopenharmony_ci		sc->virtrptr = (u8 *)sc + offset;
9188c2ecf20Sopenharmony_ci		sc->dmarptr = dma_addr + offset;
9198c2ecf20Sopenharmony_ci		sc->rdatasize = rdatasize;
9208c2ecf20Sopenharmony_ci		sc->status_word = (u64 *)((u8 *)(sc->virtrptr) + rdatasize - 8);
9218c2ecf20Sopenharmony_ci	}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	return sc;
9248c2ecf20Sopenharmony_ci}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_civoid octeon_free_soft_command(struct octeon_device *oct,
9278c2ecf20Sopenharmony_ci			      struct octeon_soft_command *sc)
9288c2ecf20Sopenharmony_ci{
9298c2ecf20Sopenharmony_ci	spin_lock_bh(&oct->sc_buf_pool.lock);
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	list_add_tail(&sc->node, &oct->sc_buf_pool.head);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	atomic_dec(&oct->sc_buf_pool.alloc_buf_count);
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	spin_unlock_bh(&oct->sc_buf_pool.lock);
9368c2ecf20Sopenharmony_ci}
937