162306a36Sopenharmony_ci/***********************license start*************** 262306a36Sopenharmony_ci * Author: Cavium Networks 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Contact: support@caviumnetworks.com 562306a36Sopenharmony_ci * This file is part of the OCTEON SDK 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2003-2008 Cavium Networks 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 * You should have received a copy of the GNU General Public License 2062306a36Sopenharmony_ci * along with this file; if not, write to the Free Software 2162306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 2262306a36Sopenharmony_ci * or visit http://www.gnu.org/licenses/. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * This file may also be available under a different license from Cavium. 2562306a36Sopenharmony_ci * Contact Cavium Networks for more information 2662306a36Sopenharmony_ci ***********************license end**************************************/ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Support functions for managing command queues used for 3062306a36Sopenharmony_ci * various hardware blocks. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <linux/kernel.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <asm/octeon/octeon.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <asm/octeon/cvmx-config.h> 3862306a36Sopenharmony_ci#include <asm/octeon/cvmx-fpa.h> 3962306a36Sopenharmony_ci#include <asm/octeon/cvmx-cmd-queue.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include <asm/octeon/cvmx-npei-defs.h> 4262306a36Sopenharmony_ci#include <asm/octeon/cvmx-pexp-defs.h> 4362306a36Sopenharmony_ci#include <asm/octeon/cvmx-pko-defs.h> 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * This application uses this pointer to access the global queue 4762306a36Sopenharmony_ci * state. It points to a bootmem named block. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci__cvmx_cmd_queue_all_state_t *__cvmx_cmd_queue_state_ptr; 5062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__cvmx_cmd_queue_state_ptr); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * Initialize the Global queue state pointer. 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cistatic cvmx_cmd_queue_result_t __cvmx_cmd_queue_init_state_ptr(void) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci char *alloc_name = "cvmx_cmd_queues"; 6062306a36Sopenharmony_ci extern uint64_t octeon_reserve32_memory; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (likely(__cvmx_cmd_queue_state_ptr)) 6362306a36Sopenharmony_ci return CVMX_CMD_QUEUE_SUCCESS; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (octeon_reserve32_memory) 6662306a36Sopenharmony_ci __cvmx_cmd_queue_state_ptr = 6762306a36Sopenharmony_ci cvmx_bootmem_alloc_named_range(sizeof(*__cvmx_cmd_queue_state_ptr), 6862306a36Sopenharmony_ci octeon_reserve32_memory, 6962306a36Sopenharmony_ci octeon_reserve32_memory + 7062306a36Sopenharmony_ci (CONFIG_CAVIUM_RESERVE32 << 7162306a36Sopenharmony_ci 20) - 1, 128, alloc_name); 7262306a36Sopenharmony_ci else 7362306a36Sopenharmony_ci __cvmx_cmd_queue_state_ptr = 7462306a36Sopenharmony_ci cvmx_bootmem_alloc_named(sizeof(*__cvmx_cmd_queue_state_ptr), 7562306a36Sopenharmony_ci 128, 7662306a36Sopenharmony_ci alloc_name); 7762306a36Sopenharmony_ci if (__cvmx_cmd_queue_state_ptr) 7862306a36Sopenharmony_ci memset(__cvmx_cmd_queue_state_ptr, 0, 7962306a36Sopenharmony_ci sizeof(*__cvmx_cmd_queue_state_ptr)); 8062306a36Sopenharmony_ci else { 8162306a36Sopenharmony_ci struct cvmx_bootmem_named_block_desc *block_desc = 8262306a36Sopenharmony_ci cvmx_bootmem_find_named_block(alloc_name); 8362306a36Sopenharmony_ci if (block_desc) 8462306a36Sopenharmony_ci __cvmx_cmd_queue_state_ptr = 8562306a36Sopenharmony_ci cvmx_phys_to_ptr(block_desc->base_addr); 8662306a36Sopenharmony_ci else { 8762306a36Sopenharmony_ci cvmx_dprintf 8862306a36Sopenharmony_ci ("ERROR: cvmx_cmd_queue_initialize: Unable to get named block %s.\n", 8962306a36Sopenharmony_ci alloc_name); 9062306a36Sopenharmony_ci return CVMX_CMD_QUEUE_NO_MEMORY; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci return CVMX_CMD_QUEUE_SUCCESS; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci * Initialize a command queue for use. The initial FPA buffer is 9862306a36Sopenharmony_ci * allocated and the hardware unit is configured to point to the 9962306a36Sopenharmony_ci * new command queue. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * @queue_id: Hardware command queue to initialize. 10262306a36Sopenharmony_ci * @max_depth: Maximum outstanding commands that can be queued. 10362306a36Sopenharmony_ci * @fpa_pool: FPA pool the command queues should come from. 10462306a36Sopenharmony_ci * @pool_size: Size of each buffer in the FPA pool (bytes) 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_cicvmx_cmd_queue_result_t cvmx_cmd_queue_initialize(cvmx_cmd_queue_id_t queue_id, 10962306a36Sopenharmony_ci int max_depth, int fpa_pool, 11062306a36Sopenharmony_ci int pool_size) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci __cvmx_cmd_queue_state_t *qstate; 11362306a36Sopenharmony_ci cvmx_cmd_queue_result_t result = __cvmx_cmd_queue_init_state_ptr(); 11462306a36Sopenharmony_ci if (result != CVMX_CMD_QUEUE_SUCCESS) 11562306a36Sopenharmony_ci return result; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci qstate = __cvmx_cmd_queue_get_state(queue_id); 11862306a36Sopenharmony_ci if (qstate == NULL) 11962306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * We artificially limit max_depth to 1<<20 words. It is an 12362306a36Sopenharmony_ci * arbitrary limit. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH) { 12662306a36Sopenharmony_ci if ((max_depth < 0) || (max_depth > 1 << 20)) 12762306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 12862306a36Sopenharmony_ci } else if (max_depth != 0) 12962306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if ((fpa_pool < 0) || (fpa_pool > 7)) 13262306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 13362306a36Sopenharmony_ci if ((pool_size < 128) || (pool_size > 65536)) 13462306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* See if someone else has already initialized the queue */ 13762306a36Sopenharmony_ci if (qstate->base_ptr_div128) { 13862306a36Sopenharmony_ci if (max_depth != (int)qstate->max_depth) { 13962306a36Sopenharmony_ci cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " 14062306a36Sopenharmony_ci "Queue already initialized with different " 14162306a36Sopenharmony_ci "max_depth (%d).\n", 14262306a36Sopenharmony_ci (int)qstate->max_depth); 14362306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci if (fpa_pool != qstate->fpa_pool) { 14662306a36Sopenharmony_ci cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " 14762306a36Sopenharmony_ci "Queue already initialized with different " 14862306a36Sopenharmony_ci "FPA pool (%u).\n", 14962306a36Sopenharmony_ci qstate->fpa_pool); 15062306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci if ((pool_size >> 3) - 1 != qstate->pool_size_m1) { 15362306a36Sopenharmony_ci cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " 15462306a36Sopenharmony_ci "Queue already initialized with different " 15562306a36Sopenharmony_ci "FPA pool size (%u).\n", 15662306a36Sopenharmony_ci (qstate->pool_size_m1 + 1) << 3); 15762306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci CVMX_SYNCWS; 16062306a36Sopenharmony_ci return CVMX_CMD_QUEUE_ALREADY_SETUP; 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci union cvmx_fpa_ctl_status status; 16362306a36Sopenharmony_ci void *buffer; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci status.u64 = cvmx_read_csr(CVMX_FPA_CTL_STATUS); 16662306a36Sopenharmony_ci if (!status.s.enb) { 16762306a36Sopenharmony_ci cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " 16862306a36Sopenharmony_ci "FPA is not enabled.\n"); 16962306a36Sopenharmony_ci return CVMX_CMD_QUEUE_NO_MEMORY; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci buffer = cvmx_fpa_alloc(fpa_pool); 17262306a36Sopenharmony_ci if (buffer == NULL) { 17362306a36Sopenharmony_ci cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " 17462306a36Sopenharmony_ci "Unable to allocate initial buffer.\n"); 17562306a36Sopenharmony_ci return CVMX_CMD_QUEUE_NO_MEMORY; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci memset(qstate, 0, sizeof(*qstate)); 17962306a36Sopenharmony_ci qstate->max_depth = max_depth; 18062306a36Sopenharmony_ci qstate->fpa_pool = fpa_pool; 18162306a36Sopenharmony_ci qstate->pool_size_m1 = (pool_size >> 3) - 1; 18262306a36Sopenharmony_ci qstate->base_ptr_div128 = cvmx_ptr_to_phys(buffer) / 128; 18362306a36Sopenharmony_ci /* 18462306a36Sopenharmony_ci * We zeroed the now serving field so we need to also 18562306a36Sopenharmony_ci * zero the ticket. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci __cvmx_cmd_queue_state_ptr-> 18862306a36Sopenharmony_ci ticket[__cvmx_cmd_queue_get_index(queue_id)] = 0; 18962306a36Sopenharmony_ci CVMX_SYNCWS; 19062306a36Sopenharmony_ci return CVMX_CMD_QUEUE_SUCCESS; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci * Shutdown a queue a free it's command buffers to the FPA. The 19662306a36Sopenharmony_ci * hardware connected to the queue must be stopped before this 19762306a36Sopenharmony_ci * function is called. 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * @queue_id: Queue to shutdown 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_cicvmx_cmd_queue_result_t cvmx_cmd_queue_shutdown(cvmx_cmd_queue_id_t queue_id) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); 20662306a36Sopenharmony_ci if (qptr == NULL) { 20762306a36Sopenharmony_ci cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Unable to " 20862306a36Sopenharmony_ci "get queue information.\n"); 20962306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (cvmx_cmd_queue_length(queue_id) > 0) { 21362306a36Sopenharmony_ci cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Queue still " 21462306a36Sopenharmony_ci "has data in it.\n"); 21562306a36Sopenharmony_ci return CVMX_CMD_QUEUE_FULL; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci __cvmx_cmd_queue_lock(queue_id, qptr); 21962306a36Sopenharmony_ci if (qptr->base_ptr_div128) { 22062306a36Sopenharmony_ci cvmx_fpa_free(cvmx_phys_to_ptr 22162306a36Sopenharmony_ci ((uint64_t) qptr->base_ptr_div128 << 7), 22262306a36Sopenharmony_ci qptr->fpa_pool, 0); 22362306a36Sopenharmony_ci qptr->base_ptr_div128 = 0; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return CVMX_CMD_QUEUE_SUCCESS; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* 23162306a36Sopenharmony_ci * Return the number of command words pending in the queue. This 23262306a36Sopenharmony_ci * function may be relatively slow for some hardware units. 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * @queue_id: Hardware command queue to query 23562306a36Sopenharmony_ci * 23662306a36Sopenharmony_ci * Returns Number of outstanding commands 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ciint cvmx_cmd_queue_length(cvmx_cmd_queue_id_t queue_id) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci if (CVMX_ENABLE_PARAMETER_CHECKING) { 24162306a36Sopenharmony_ci if (__cvmx_cmd_queue_get_state(queue_id) == NULL) 24262306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* 24662306a36Sopenharmony_ci * The cast is here so gcc with check that all values in the 24762306a36Sopenharmony_ci * cvmx_cmd_queue_id_t enumeration are here. 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci switch ((cvmx_cmd_queue_id_t) (queue_id & 0xff0000)) { 25062306a36Sopenharmony_ci case CVMX_CMD_QUEUE_PKO_BASE: 25162306a36Sopenharmony_ci /* 25262306a36Sopenharmony_ci * FIXME: Need atomic lock on 25362306a36Sopenharmony_ci * CVMX_PKO_REG_READ_IDX. Right now we are normally 25462306a36Sopenharmony_ci * called with the queue lock, so that is a SLIGHT 25562306a36Sopenharmony_ci * amount of protection. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue_id & 0xffff); 25862306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) { 25962306a36Sopenharmony_ci union cvmx_pko_mem_debug9 debug9; 26062306a36Sopenharmony_ci debug9.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG9); 26162306a36Sopenharmony_ci return debug9.cn38xx.doorbell; 26262306a36Sopenharmony_ci } else { 26362306a36Sopenharmony_ci union cvmx_pko_mem_debug8 debug8; 26462306a36Sopenharmony_ci debug8.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG8); 26562306a36Sopenharmony_ci return debug8.cn50xx.doorbell; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci case CVMX_CMD_QUEUE_ZIP: 26862306a36Sopenharmony_ci case CVMX_CMD_QUEUE_DFA: 26962306a36Sopenharmony_ci case CVMX_CMD_QUEUE_RAID: 27062306a36Sopenharmony_ci /* FIXME: Implement other lengths */ 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci case CVMX_CMD_QUEUE_DMA_BASE: 27362306a36Sopenharmony_ci { 27462306a36Sopenharmony_ci union cvmx_npei_dmax_counts dmax_counts; 27562306a36Sopenharmony_ci dmax_counts.u64 = 27662306a36Sopenharmony_ci cvmx_read_csr(CVMX_PEXP_NPEI_DMAX_COUNTS 27762306a36Sopenharmony_ci (queue_id & 0x7)); 27862306a36Sopenharmony_ci return dmax_counts.s.dbell; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci case CVMX_CMD_QUEUE_END: 28162306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci return CVMX_CMD_QUEUE_INVALID_PARAM; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* 28762306a36Sopenharmony_ci * Return the command buffer to be written to. The purpose of this 28862306a36Sopenharmony_ci * function is to allow CVMX routine access t othe low level buffer 28962306a36Sopenharmony_ci * for initial hardware setup. User applications should not call this 29062306a36Sopenharmony_ci * function directly. 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * @queue_id: Command queue to query 29362306a36Sopenharmony_ci * 29462306a36Sopenharmony_ci * Returns Command buffer or NULL on failure 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_civoid *cvmx_cmd_queue_buffer(cvmx_cmd_queue_id_t queue_id) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); 29962306a36Sopenharmony_ci if (qptr && qptr->base_ptr_div128) 30062306a36Sopenharmony_ci return cvmx_phys_to_ptr((uint64_t) qptr->base_ptr_div128 << 7); 30162306a36Sopenharmony_ci else 30262306a36Sopenharmony_ci return NULL; 30362306a36Sopenharmony_ci} 304