/***********************license start*************** * Author: Cavium Networks * * Contact: support@caviumnetworks.com * This file is part of the OCTEON SDK * * Copyright (c) 2003-2008 Cavium Networks * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as * published by the Free Software Foundation. * * This file is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this file; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * or visit http://www.gnu.org/licenses/. * * This file may also be available under a different license from Cavium. * Contact Cavium Networks for more information ***********************license end**************************************/ /* * Support functions for managing command queues used for * various hardware blocks. */ #include <linux/kernel.h> #include <asm/octeon/octeon.h> #include <asm/octeon/cvmx-config.h> #include <asm/octeon/cvmx-fpa.h> #include <asm/octeon/cvmx-cmd-queue.h> #include <asm/octeon/cvmx-npei-defs.h> #include <asm/octeon/cvmx-pexp-defs.h> #include <asm/octeon/cvmx-pko-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_ptr; EXPORT_SYMBOL_GPL(__cvmx_cmd_queue_state_ptr); /** * Initialize the Global queue state pointer. * * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code */ static cvmx_cmd_queue_result_t __cvmx_cmd_queue_init_state_ptr(void) { char *alloc_name = "cvmx_cmd_queues"; #if defined(CONFIG_CAVIUM_RESERVE32) && CONFIG_CAVIUM_RESERVE32 extern uint64_t octeon_reserve32_memory; #endif if (likely(__cvmx_cmd_queue_state_ptr)) return CVMX_CMD_QUEUE_SUCCESS; #if defined(CONFIG_CAVIUM_RESERVE32) && CONFIG_CAVIUM_RESERVE32 if (octeon_reserve32_memory) __cvmx_cmd_queue_state_ptr = cvmx_bootmem_alloc_named_range(sizeof(*__cvmx_cmd_queue_state_ptr), octeon_reserve32_memory, octeon_reserve32_memory + (CONFIG_CAVIUM_RESERVE32 << 20) - 1, 128, alloc_name); else #endif __cvmx_cmd_queue_state_ptr = cvmx_bootmem_alloc_named(sizeof(*__cvmx_cmd_queue_state_ptr), 128, alloc_name); if (__cvmx_cmd_queue_state_ptr) memset(__cvmx_cmd_queue_state_ptr, 0, sizeof(*__cvmx_cmd_queue_state_ptr)); else { struct cvmx_bootmem_named_block_desc *block_desc = cvmx_bootmem_find_named_block(alloc_name); if (block_desc) __cvmx_cmd_queue_state_ptr = cvmx_phys_to_ptr(block_desc->base_addr); else { cvmx_dprintf ("ERROR: cvmx_cmd_queue_initialize: Unable to get named block %s.\n", 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. * * @queue_id: Hardware command queue to initialize. * @max_depth: Maximum outstanding commands that can be queued. * @fpa_pool: FPA pool the command queues should come from. * @pool_size: Size of each buffer in the FPA pool (bytes) * * Returns 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 = __cvmx_cmd_queue_init_state_ptr(); if (result != CVMX_CMD_QUEUE_SUCCESS) return result; qstate = __cvmx_cmd_queue_get_state(queue_id); if (qstate == NULL) 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; if ((fpa_pool < 0) || (fpa_pool > 7)) return CVMX_CMD_QUEUE_INVALID_PARAM; if ((pool_size < 128) || (pool_size > 65536)) return CVMX_CMD_QUEUE_INVALID_PARAM; /* See if someone else has already initialized the queue */ if (qstate->base_ptr_div128) { if (max_depth != (int)qstate->max_depth) { cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " "Queue already initialized with different " "max_depth (%d).\n", (int)qstate->max_depth); return CVMX_CMD_QUEUE_INVALID_PARAM; } if (fpa_pool != qstate->fpa_pool) { cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " "Queue already initialized with different " "FPA pool (%u).\n", qstate->fpa_pool); return CVMX_CMD_QUEUE_INVALID_PARAM; } if ((pool_size >> 3) - 1 != qstate->pool_size_m1) { cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " "Queue already initialized with different " "FPA pool size (%u).\n", (qstate->pool_size_m1 + 1) << 3); return CVMX_CMD_QUEUE_INVALID_PARAM; } CVMX_SYNCWS; return CVMX_CMD_QUEUE_ALREADY_SETUP; } else { union cvmx_fpa_ctl_status status; void *buffer; status.u64 = cvmx_read_csr(CVMX_FPA_CTL_STATUS); if (!status.s.enb) { cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " "FPA is not enabled.\n"); return CVMX_CMD_QUEUE_NO_MEMORY; } buffer = cvmx_fpa_alloc(fpa_pool); if (buffer == NULL) { cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " "Unable to allocate initial buffer.\n"); return CVMX_CMD_QUEUE_NO_MEMORY; } memset(qstate, 0, sizeof(*qstate)); qstate->max_depth = max_depth; qstate->fpa_pool = fpa_pool; qstate->pool_size_m1 = (pool_size >> 3) - 1; qstate->base_ptr_div128 = cvmx_ptr_to_phys(buffer) / 128; /* * We zeroed the now serving field so we need to also * zero the ticket. */ __cvmx_cmd_queue_state_ptr-> ticket[__cvmx_cmd_queue_get_index(queue_id)] = 0; CVMX_SYNCWS; 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. * * @queue_id: Queue to shutdown * * Returns 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); if (qptr == NULL) { cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Unable to " "get queue information.\n"); return CVMX_CMD_QUEUE_INVALID_PARAM; } if (cvmx_cmd_queue_length(queue_id) > 0) { cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Queue still " "has data in it.\n"); return CVMX_CMD_QUEUE_FULL; } __cvmx_cmd_queue_lock(queue_id, qptr); if (qptr->base_ptr_div128) { cvmx_fpa_free(cvmx_phys_to_ptr ((uint64_t) qptr->base_ptr_div128 << 7), qptr->fpa_pool, 0); qptr->base_ptr_div128 = 0; } __cvmx_cmd_queue_unlock(qptr); 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. * * @queue_id: Hardware command queue to query * * Returns Number of outstanding commands */ int cvmx_cmd_queue_length(cvmx_cmd_queue_id_t queue_id) { if (CVMX_ENABLE_PARAMETER_CHECKING) { if (__cvmx_cmd_queue_get_state(queue_id) == NULL) 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: /* * FIXME: 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. */ cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue_id & 0xffff); if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) { union cvmx_pko_mem_debug9 debug9; debug9.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG9); return debug9.cn38xx.doorbell; } else { union cvmx_pko_mem_debug8 debug8; debug8.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG8); return debug8.cn58xx.doorbell; } case CVMX_CMD_QUEUE_ZIP: case CVMX_CMD_QUEUE_DFA: case CVMX_CMD_QUEUE_RAID: /* FIXME: Implement other lengths */ return 0; case CVMX_CMD_QUEUE_DMA_BASE: { union cvmx_npei_dmax_counts dmax_counts; dmax_counts.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DMAX_COUNTS (queue_id & 0x7)); return dmax_counts.s.dbell; } 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 t othe low level buffer * for initial hardware setup. User applications should not call this * function directly. * * @queue_id: Command queue to query * * Returns 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_ptr_div128) return cvmx_phys_to_ptr((uint64_t) qptr->base_ptr_div128 << 7); else return NULL; }