[PATCH 25/52] mips: octeon: Add cvmx-cmd-queue.c

Stefan Roese sr at denx.de
Wed Mar 30 12:07:01 CEST 2022


From: Aaron Williams <awilliams at marvell.com>

Import cvmx-cmd-queue.c from 2013 U-Boot. It will be used by the later
added drivers to support networking on the MIPS Octeon II / III
platforms.

Signed-off-by: Aaron Williams <awilliams at marvell.com>
Signed-off-by: Stefan Roese <sr at denx.de>
---
 arch/mips/mach-octeon/cvmx-cmd-queue.c | 449 +++++++++++++++++++++++++
 1 file changed, 449 insertions(+)
 create mode 100644 arch/mips/mach-octeon/cvmx-cmd-queue.c

diff --git a/arch/mips/mach-octeon/cvmx-cmd-queue.c b/arch/mips/mach-octeon/cvmx-cmd-queue.c
new file mode 100644
index 000000000000..041f2390ca18
--- /dev/null
+++ b/arch/mips/mach-octeon/cvmx-cmd-queue.c
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2022 Marvell International Ltd.
+ *
+ * Support functions for managing command queues used for
+ * various hardware blocks.
+ */
+
+#include <errno.h>
+#include <log.h>
+#include <time.h>
+#include <linux/delay.h>
+
+#include <mach/cvmx-regs.h>
+#include <mach/cvmx-csr.h>
+#include <mach/cvmx-bootmem.h>
+#include <mach/octeon-model.h>
+#include <mach/cvmx-fuse.h>
+#include <mach/octeon-feature.h>
+#include <mach/cvmx-qlm.h>
+#include <mach/octeon_qlm.h>
+#include <mach/cvmx-pcie.h>
+#include <mach/cvmx-coremask.h>
+
+#include <mach/cvmx-fpa.h>
+#include <mach/cvmx-cmd-queue.h>
+
+#include <mach/cvmx-agl-defs.h>
+#include <mach/cvmx-bgxx-defs.h>
+#include <mach/cvmx-ciu-defs.h>
+#include <mach/cvmx-gmxx-defs.h>
+#include <mach/cvmx-gserx-defs.h>
+#include <mach/cvmx-ilk-defs.h>
+#include <mach/cvmx-ipd-defs.h>
+#include <mach/cvmx-pcsx-defs.h>
+#include <mach/cvmx-pcsxx-defs.h>
+#include <mach/cvmx-pki-defs.h>
+#include <mach/cvmx-pko-defs.h>
+#include <mach/cvmx-xcv-defs.h>
+
+#include <mach/cvmx-hwpko.h>
+#include <mach/cvmx-ilk.h>
+#include <mach/cvmx-pki.h>
+#include <mach/cvmx-pko3.h>
+#include <mach/cvmx-pko3-queue.h>
+#include <mach/cvmx-pko3-resources.h>
+
+#include <mach/cvmx-helper.h>
+#include <mach/cvmx-helper-board.h>
+#include <mach/cvmx-helper-cfg.h>
+
+#include <mach/cvmx-helper-bgx.h>
+#include <mach/cvmx-helper-cfg.h>
+#include <mach/cvmx-helper-util.h>
+#include <mach/cvmx-helper-pki.h>
+
+#include <mach/cvmx-helper-util.h>
+#include <mach/cvmx-dpi-defs.h>
+#include <mach/cvmx-npei-defs.h>
+#include <mach/cvmx-pexp-defs.h>
+
+/**
+ * This application uses this pointer to access the global queue
+ * state. It points to a bootmem named block.
+ */
+__cvmx_cmd_queue_all_state_t *__cvmx_cmd_queue_state_ptrs[CVMX_MAX_NODES];
+
+/**
+ * @INTERNAL
+ * Initialize the Global queue state pointer.
+ *
+ * @return CVMX_CMD_QUEUE_SUCCESS or a failure code
+ */
+cvmx_cmd_queue_result_t __cvmx_cmd_queue_init_state_ptr(unsigned int node)
+{
+	const char *alloc_name = "cvmx_cmd_queues\0\0";
+	char s[4] = "_0";
+	const struct cvmx_bootmem_named_block_desc *block_desc = NULL;
+	unsigned int size;
+	u64 paddr_min = 0, paddr_max = 0;
+	void *ptr;
+
+	if (cvmx_likely(__cvmx_cmd_queue_state_ptrs[node]))
+		return CVMX_CMD_QUEUE_SUCCESS;
+
+	/* Add node# to block name */
+	if (node > 0) {
+		s[1] += node;
+		strcat((char *)alloc_name, s);
+	}
+
+	/* Find the named block in case it has been created already */
+	block_desc = cvmx_bootmem_find_named_block(alloc_name);
+	if (block_desc) {
+		__cvmx_cmd_queue_state_ptrs[node] =
+			(__cvmx_cmd_queue_all_state_t *)cvmx_phys_to_ptr(
+				block_desc->base_addr);
+		return CVMX_CMD_QUEUE_SUCCESS;
+	}
+
+	size = sizeof(*__cvmx_cmd_queue_state_ptrs[node]);
+
+	/* Rest f the code is to allocate a new named block */
+
+	/* Atomically allocate named block once, and zero it by default */
+	ptr = cvmx_bootmem_alloc_named_range_once(size, paddr_min, paddr_max,
+						  128, alloc_name, NULL);
+
+	if (ptr) {
+		__cvmx_cmd_queue_state_ptrs[node] =
+			(__cvmx_cmd_queue_all_state_t *)ptr;
+	} else {
+		debug("ERROR: %s: Unable to get named block %s.\n", __func__,
+		      alloc_name);
+		return CVMX_CMD_QUEUE_NO_MEMORY;
+	}
+	return CVMX_CMD_QUEUE_SUCCESS;
+}
+
+/**
+ * Initialize a command queue for use. The initial FPA buffer is
+ * allocated and the hardware unit is configured to point to the
+ * new command queue.
+ *
+ * @param queue_id  Hardware command queue to initialize.
+ * @param max_depth Maximum outstanding commands that can be queued.
+ * @param fpa_pool  FPA pool the command queues should come from.
+ * @param pool_size Size of each buffer in the FPA pool (bytes)
+ *
+ * @return CVMX_CMD_QUEUE_SUCCESS or a failure code
+ */
+cvmx_cmd_queue_result_t cvmx_cmd_queue_initialize(cvmx_cmd_queue_id_t queue_id,
+						  int max_depth, int fpa_pool,
+						  int pool_size)
+{
+	__cvmx_cmd_queue_state_t *qstate;
+	cvmx_cmd_queue_result_t result;
+	unsigned int node;
+	unsigned int index;
+	int fpa_pool_min, fpa_pool_max;
+	union cvmx_fpa_ctl_status status;
+	void *buffer;
+
+	node = __cvmx_cmd_queue_get_node(queue_id);
+
+	index = __cvmx_cmd_queue_get_index(queue_id);
+	if (index >= NUM_ELEMENTS(__cvmx_cmd_queue_state_ptrs[node]->state)) {
+		printf("ERROR: %s: queue %#x out of range\n", __func__,
+		       queue_id);
+		return CVMX_CMD_QUEUE_INVALID_PARAM;
+	}
+
+	result = __cvmx_cmd_queue_init_state_ptr(node);
+	if (result != CVMX_CMD_QUEUE_SUCCESS)
+		return result;
+
+	qstate = __cvmx_cmd_queue_get_state(queue_id);
+	if (!qstate)
+		return CVMX_CMD_QUEUE_INVALID_PARAM;
+
+	/*
+	 * We artificially limit max_depth to 1<<20 words. It is an
+	 * arbitrary limit.
+	 */
+	if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH) {
+		if (max_depth < 0 || max_depth > 1 << 20)
+			return CVMX_CMD_QUEUE_INVALID_PARAM;
+	} else if (max_depth != 0) {
+		return CVMX_CMD_QUEUE_INVALID_PARAM;
+	}
+
+	/* CVMX_FPA_NUM_POOLS maps to cvmx_fpa3_num_auras for FPA3 */
+	fpa_pool_min = node << 10;
+	fpa_pool_max = fpa_pool_min + CVMX_FPA_NUM_POOLS;
+
+	if (fpa_pool < fpa_pool_min || fpa_pool >= fpa_pool_max)
+		return CVMX_CMD_QUEUE_INVALID_PARAM;
+
+	if (pool_size < 128 || pool_size > (1 << 17))
+		return CVMX_CMD_QUEUE_INVALID_PARAM;
+
+	if (pool_size & 3)
+		debug("WARNING: %s: pool_size %d not multiple of 8\n", __func__,
+		      pool_size);
+
+	/* See if someone else has already initialized the queue */
+	if (qstate->base_paddr) {
+		int depth;
+		static const char emsg[] = /* Common error message part */
+			"Queue already initialized with different ";
+
+		depth = (max_depth + qstate->pool_size_m1 - 1) /
+			qstate->pool_size_m1;
+		if (depth != qstate->max_depth) {
+			depth = qstate->max_depth * qstate->pool_size_m1;
+			debug("ERROR: %s: %s max_depth (%d).\n", __func__, emsg,
+			      depth);
+			return CVMX_CMD_QUEUE_INVALID_PARAM;
+		}
+		if (fpa_pool != qstate->fpa_pool) {
+			debug("ERROR: %s: %s FPA pool (%d).\n", __func__, emsg,
+			      (int)qstate->fpa_pool);
+			return CVMX_CMD_QUEUE_INVALID_PARAM;
+		}
+		if ((pool_size >> 3) - 1 != qstate->pool_size_m1) {
+			debug("ERROR: %s: %s FPA pool size (%u).\n", __func__,
+			      emsg, (qstate->pool_size_m1 + 1) << 3);
+			return CVMX_CMD_QUEUE_INVALID_PARAM;
+		}
+		return CVMX_CMD_QUEUE_ALREADY_SETUP;
+	}
+
+	if (!(octeon_has_feature(OCTEON_FEATURE_FPA3))) {
+		status.u64 = csr_rd(CVMX_FPA_CTL_STATUS);
+		if (!status.s.enb) {
+			debug("ERROR: %s: FPA is not enabled.\n",
+			      __func__);
+			return CVMX_CMD_QUEUE_NO_MEMORY;
+		}
+	}
+	buffer = cvmx_fpa_alloc(fpa_pool);
+	if (!buffer) {
+		debug("ERROR: %s: allocating first buffer.\n", __func__);
+		return CVMX_CMD_QUEUE_NO_MEMORY;
+	}
+
+	index = (pool_size >> 3) - 1;
+	qstate->pool_size_m1 = index;
+	qstate->max_depth = (max_depth + index - 1) / index;
+	qstate->index = 0;
+	qstate->fpa_pool = fpa_pool;
+	qstate->base_paddr = cvmx_ptr_to_phys(buffer);
+
+	/* Initialize lock */
+	__cvmx_cmd_queue_lock_init(queue_id);
+	return CVMX_CMD_QUEUE_SUCCESS;
+}
+
+/**
+ * Shutdown a queue a free it's command buffers to the FPA. The
+ * hardware connected to the queue must be stopped before this
+ * function is called.
+ *
+ * @param queue_id Queue to shutdown
+ *
+ * @return CVMX_CMD_QUEUE_SUCCESS or a failure code
+ */
+cvmx_cmd_queue_result_t cvmx_cmd_queue_shutdown(cvmx_cmd_queue_id_t queue_id)
+{
+	__cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id);
+
+	/* FIXME: This will not complain if the queue was never initialized */
+	if (!qptr) {
+		debug("ERROR: %s: Unable to get queue information.\n",
+		      __func__);
+		return CVMX_CMD_QUEUE_INVALID_PARAM;
+	}
+
+	if (cvmx_cmd_queue_length(queue_id) > 0) {
+		debug("ERROR: %s: Queue still has data in it.\n", __func__);
+		return CVMX_CMD_QUEUE_FULL;
+	}
+
+	__cvmx_cmd_queue_lock(queue_id);
+	if (qptr->base_paddr) {
+		cvmx_fpa_free(cvmx_phys_to_ptr((u64)qptr->base_paddr),
+			      qptr->fpa_pool, 0);
+		qptr->base_paddr = 0;
+	}
+	__cvmx_cmd_queue_unlock(queue_id);
+
+	return CVMX_CMD_QUEUE_SUCCESS;
+}
+
+/**
+ * Return the number of command words pending in the queue. This
+ * function may be relatively slow for some hardware units.
+ *
+ * @param queue_id Hardware command queue to query
+ *
+ * @return Number of outstanding commands
+ */
+int cvmx_cmd_queue_length(cvmx_cmd_queue_id_t queue_id)
+{
+	union cvmx_dpi_dmax_counts dmax_counts;
+	union cvmx_pko_mem_debug8 debug8;
+
+	if (!__cvmx_cmd_queue_get_state(queue_id))
+		return CVMX_CMD_QUEUE_INVALID_PARAM;
+
+	/*
+	 * The cast is here so gcc with check that all values in the
+	 * cvmx_cmd_queue_id_t enumeration are here.
+	 */
+	switch ((cvmx_cmd_queue_id_t)(queue_id & 0xff0000)) {
+	case CVMX_CMD_QUEUE_PKO_BASE:
+		/*
+		 * Really need atomic lock on
+		 * CVMX_PKO_REG_READ_IDX. Right now we are normally
+		 * called with the queue lock, so that is a SLIGHT
+		 * amount of protection.
+		 */
+		csr_wr(CVMX_PKO_REG_READ_IDX, queue_id & 0xffff);
+		debug8.u64 = csr_rd(CVMX_PKO_MEM_DEBUG8);
+		if (octeon_has_feature(OCTEON_FEATURE_PKND))
+			return debug8.cn68xx.doorbell;
+		else
+			return debug8.cn58xx.doorbell;
+	case CVMX_CMD_QUEUE_ZIP:
+	case CVMX_CMD_QUEUE_DFA:
+	case CVMX_CMD_QUEUE_HNA:
+	case CVMX_CMD_QUEUE_RAID:
+		/* Still need to implement other lengths */
+		return 0;
+	case CVMX_CMD_QUEUE_DMA_BASE:
+		if (octeon_has_feature(OCTEON_FEATURE_NPEI)) {
+			dmax_counts.u64 = csr_rd(
+				CVMX_PEXP_NPEI_DMAX_COUNTS(queue_id & 0x7));
+			return dmax_counts.s.dbell;
+		}
+
+		dmax_counts.u64 = csr_rd(CVMX_DPI_DMAX_COUNTS(queue_id & 0x7));
+		return dmax_counts.s.dbell;
+	case CVMX_CMD_QUEUE_BCH:
+		/* Not available */
+		return 0;
+	case CVMX_CMD_QUEUE_END:
+		return CVMX_CMD_QUEUE_INVALID_PARAM;
+	}
+	return CVMX_CMD_QUEUE_INVALID_PARAM;
+}
+
+/**
+ * Return the command buffer to be written to. The purpose of this
+ * function is to allow CVMX routine access to the low level buffer
+ * for initial hardware setup. User applications should not call this
+ * function directly.
+ *
+ * @param queue_id Command queue to query
+ *
+ * @return Command buffer or NULL on failure
+ */
+void *cvmx_cmd_queue_buffer(cvmx_cmd_queue_id_t queue_id)
+{
+	__cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id);
+
+	if (qptr && qptr->base_paddr)
+		return cvmx_phys_to_ptr((u64)qptr->base_paddr);
+	else
+		return NULL;
+}
+
+static u64 *__cvmx_cmd_queue_add_blk(__cvmx_cmd_queue_state_t *qptr)
+{
+	u64 *cmd_ptr;
+	u64 *new_buffer;
+	u64 new_paddr;
+
+	/* Get base vaddr of current (full) block */
+	cmd_ptr = (u64 *)cvmx_phys_to_ptr((u64)qptr->base_paddr);
+
+	/* Allocate a new block from the per-queue pool */
+	new_buffer = (u64 *)cvmx_fpa_alloc(qptr->fpa_pool);
+
+	/* Check for allocation failure */
+	if (cvmx_unlikely(!new_buffer))
+		return NULL;
+
+	/* Zero out the new block link pointer,
+	 * in case this block will be filled to the rim
+	 */
+	new_buffer[qptr->pool_size_m1] = ~0ull;
+
+	/* Get physical address of the new buffer */
+	new_paddr = cvmx_ptr_to_phys(new_buffer);
+
+	/* Store the physical link address at the end of current full block */
+	cmd_ptr[qptr->pool_size_m1] = new_paddr;
+
+	/* Store the physical address in the queue state structure */
+	qptr->base_paddr = new_paddr;
+	qptr->index = 0;
+
+	/* Return the virtual base of the new block */
+	return new_buffer;
+}
+
+/**
+ * @INTERNAL
+ * Add command words into a queue, handles all the corener cases
+ * where only some of the words might fit into the current block,
+ * and a new block may need to be allocated.
+ * Locking and argument checks are done in the front-end in-line
+ * functions that call this one for the rare corner cases.
+ */
+cvmx_cmd_queue_result_t
+__cvmx_cmd_queue_write_raw(cvmx_cmd_queue_id_t queue_id,
+			   __cvmx_cmd_queue_state_t *qptr, int cmd_count,
+			   const u64 *cmds)
+{
+	u64 *cmd_ptr;
+	unsigned int index;
+
+	cmd_ptr = (u64 *)cvmx_phys_to_ptr((u64)qptr->base_paddr);
+	index = qptr->index;
+
+	/* Enforce queue depth limit, if enabled, once per block */
+	if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH && cvmx_unlikely(qptr->max_depth)) {
+		unsigned int depth = cvmx_cmd_queue_length(queue_id);
+
+		depth /= qptr->pool_size_m1;
+
+		if (cvmx_unlikely(depth > qptr->max_depth))
+			return CVMX_CMD_QUEUE_FULL;
+	}
+
+	/*
+	 * If the block allocation fails, even the words that we wrote
+	 * to the current block will not count because the 'index' will
+	 * not be comitted.
+	 * The loop is run 'count + 1' times to take care of the tail
+	 * case, where the buffer is full to the rim, so the link
+	 * pointer must be filled with a valid address.
+	 */
+	while (cmd_count >= 0) {
+		if (index >= qptr->pool_size_m1) {
+			/* Block is full, get another one and proceed */
+			cmd_ptr = __cvmx_cmd_queue_add_blk(qptr);
+
+			/* Baul on allocation error w/o comitting anything */
+			if (cvmx_unlikely(!cmd_ptr))
+				return CVMX_CMD_QUEUE_NO_MEMORY;
+
+			/* Reset index for start of new block */
+			index = 0;
+		}
+		/* Exit Loop on 'count + 1' iterations */
+		if (cmd_count <= 0)
+			break;
+		/* Store commands into queue block while there is space */
+		cmd_ptr[index++] = *cmds++;
+		cmd_count--;
+	} /* while cmd_count */
+
+	/* Commit added words if all is well */
+	qptr->index = index;
+
+	return CVMX_CMD_QUEUE_SUCCESS;
+}
-- 
2.35.1



More information about the U-Boot mailing list