From: Aaron Williams Date: Mon, 10 May 2021 11:45:15 +0000 (+0200) Subject: mips: octeon: Add cvmx-cmd-queue.c X-Git-Url: http://git.dujemihanovic.xyz/img/static/git-logo.png?a=commitdiff_plain;h=16c50d61eecced0a288ef2e26da5cf5e6c5428ff;p=u-boot.git mips: octeon: Add cvmx-cmd-queue.c 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 Signed-off-by: Stefan Roese --- 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 0000000000..c4b49f940b --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-cmd-queue.c @@ -0,0 +1,355 @@ +// 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * 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; +} + +/** + * 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; +}