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 * 3062306a36Sopenharmony_ci * Support functions for managing command queues used for 3162306a36Sopenharmony_ci * various hardware blocks. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * The common command queue infrastructure abstracts out the 3462306a36Sopenharmony_ci * software necessary for adding to Octeon's chained queue 3562306a36Sopenharmony_ci * structures. These structures are used for commands to the 3662306a36Sopenharmony_ci * PKO, ZIP, DFA, RAID, and DMA engine blocks. Although each 3762306a36Sopenharmony_ci * hardware unit takes commands and CSRs of different types, 3862306a36Sopenharmony_ci * they all use basic linked command buffers to store the 3962306a36Sopenharmony_ci * pending request. In general, users of the CVMX API don't 4062306a36Sopenharmony_ci * call cvmx-cmd-queue functions directly. Instead the hardware 4162306a36Sopenharmony_ci * unit specific wrapper should be used. The wrappers perform 4262306a36Sopenharmony_ci * unit specific validation and CSR writes to submit the 4362306a36Sopenharmony_ci * commands. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * Even though most software will never directly interact with 4662306a36Sopenharmony_ci * cvmx-cmd-queue, knowledge of its internal working can help 4762306a36Sopenharmony_ci * in diagnosing performance problems and help with debugging. 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * Command queue pointers are stored in a global named block 5062306a36Sopenharmony_ci * called "cvmx_cmd_queues". Except for the PKO queues, each 5162306a36Sopenharmony_ci * hardware queue is stored in its own cache line to reduce SMP 5262306a36Sopenharmony_ci * contention on spin locks. The PKO queues are stored such that 5362306a36Sopenharmony_ci * every 16th queue is next to each other in memory. This scheme 5462306a36Sopenharmony_ci * allows for queues being in separate cache lines when there 5562306a36Sopenharmony_ci * are low number of queues per port. With 16 queues per port, 5662306a36Sopenharmony_ci * the first queue for each port is in the same cache area. The 5762306a36Sopenharmony_ci * second queues for each port are in another area, etc. This 5862306a36Sopenharmony_ci * allows software to implement very efficient lockless PKO with 5962306a36Sopenharmony_ci * 16 queues per port using a minimum of cache lines per core. 6062306a36Sopenharmony_ci * All queues for a given core will be isolated in the same 6162306a36Sopenharmony_ci * cache area. 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * In addition to the memory pointer layout, cvmx-cmd-queue 6462306a36Sopenharmony_ci * provides an optimized fair ll/sc locking mechanism for the 6562306a36Sopenharmony_ci * queues. The lock uses a "ticket / now serving" model to 6662306a36Sopenharmony_ci * maintain fair order on contended locks. In addition, it uses 6762306a36Sopenharmony_ci * predicted locking time to limit cache contention. When a core 6862306a36Sopenharmony_ci * know it must wait in line for a lock, it spins on the 6962306a36Sopenharmony_ci * internal cycle counter to completely eliminate any causes of 7062306a36Sopenharmony_ci * bus traffic. 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#ifndef __CVMX_CMD_QUEUE_H__ 7562306a36Sopenharmony_ci#define __CVMX_CMD_QUEUE_H__ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#include <linux/prefetch.h> 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#include <asm/compiler.h> 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#include <asm/octeon/cvmx-fpa.h> 8262306a36Sopenharmony_ci/** 8362306a36Sopenharmony_ci * By default we disable the max depth support. Most programs 8462306a36Sopenharmony_ci * don't use it and it slows down the command queue processing 8562306a36Sopenharmony_ci * significantly. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci#ifndef CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH 8862306a36Sopenharmony_ci#define CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH 0 8962306a36Sopenharmony_ci#endif 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/** 9262306a36Sopenharmony_ci * Enumeration representing all hardware blocks that use command 9362306a36Sopenharmony_ci * queues. Each hardware block has up to 65536 sub identifiers for 9462306a36Sopenharmony_ci * multiple command queues. Not all chips support all hardware 9562306a36Sopenharmony_ci * units. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_citypedef enum { 9862306a36Sopenharmony_ci CVMX_CMD_QUEUE_PKO_BASE = 0x00000, 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define CVMX_CMD_QUEUE_PKO(queue) \ 10162306a36Sopenharmony_ci ((cvmx_cmd_queue_id_t)(CVMX_CMD_QUEUE_PKO_BASE + (0xffff&(queue)))) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci CVMX_CMD_QUEUE_ZIP = 0x10000, 10462306a36Sopenharmony_ci CVMX_CMD_QUEUE_DFA = 0x20000, 10562306a36Sopenharmony_ci CVMX_CMD_QUEUE_RAID = 0x30000, 10662306a36Sopenharmony_ci CVMX_CMD_QUEUE_DMA_BASE = 0x40000, 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define CVMX_CMD_QUEUE_DMA(queue) \ 10962306a36Sopenharmony_ci ((cvmx_cmd_queue_id_t)(CVMX_CMD_QUEUE_DMA_BASE + (0xffff&(queue)))) 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci CVMX_CMD_QUEUE_END = 0x50000, 11262306a36Sopenharmony_ci} cvmx_cmd_queue_id_t; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/** 11562306a36Sopenharmony_ci * Command write operations can fail if the command queue needs 11662306a36Sopenharmony_ci * a new buffer and the associated FPA pool is empty. It can also 11762306a36Sopenharmony_ci * fail if the number of queued command words reaches the maximum 11862306a36Sopenharmony_ci * set at initialization. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_citypedef enum { 12162306a36Sopenharmony_ci CVMX_CMD_QUEUE_SUCCESS = 0, 12262306a36Sopenharmony_ci CVMX_CMD_QUEUE_NO_MEMORY = -1, 12362306a36Sopenharmony_ci CVMX_CMD_QUEUE_FULL = -2, 12462306a36Sopenharmony_ci CVMX_CMD_QUEUE_INVALID_PARAM = -3, 12562306a36Sopenharmony_ci CVMX_CMD_QUEUE_ALREADY_SETUP = -4, 12662306a36Sopenharmony_ci} cvmx_cmd_queue_result_t; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_citypedef struct { 12962306a36Sopenharmony_ci /* You have lock when this is your ticket */ 13062306a36Sopenharmony_ci uint8_t now_serving; 13162306a36Sopenharmony_ci uint64_t unused1:24; 13262306a36Sopenharmony_ci /* Maximum outstanding command words */ 13362306a36Sopenharmony_ci uint32_t max_depth; 13462306a36Sopenharmony_ci /* FPA pool buffers come from */ 13562306a36Sopenharmony_ci uint64_t fpa_pool:3; 13662306a36Sopenharmony_ci /* Top of command buffer pointer shifted 7 */ 13762306a36Sopenharmony_ci uint64_t base_ptr_div128:29; 13862306a36Sopenharmony_ci uint64_t unused2:6; 13962306a36Sopenharmony_ci /* FPA buffer size in 64bit words minus 1 */ 14062306a36Sopenharmony_ci uint64_t pool_size_m1:13; 14162306a36Sopenharmony_ci /* Number of commands already used in buffer */ 14262306a36Sopenharmony_ci uint64_t index:13; 14362306a36Sopenharmony_ci} __cvmx_cmd_queue_state_t; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/** 14662306a36Sopenharmony_ci * This structure contains the global state of all command queues. 14762306a36Sopenharmony_ci * It is stored in a bootmem named block and shared by all 14862306a36Sopenharmony_ci * applications running on Octeon. Tickets are stored in a differnet 14962306a36Sopenharmony_ci * cache line that queue information to reduce the contention on the 15062306a36Sopenharmony_ci * ll/sc used to get a ticket. If this is not the case, the update 15162306a36Sopenharmony_ci * of queue state causes the ll/sc to fail quite often. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_citypedef struct { 15462306a36Sopenharmony_ci uint64_t ticket[(CVMX_CMD_QUEUE_END >> 16) * 256]; 15562306a36Sopenharmony_ci __cvmx_cmd_queue_state_t state[(CVMX_CMD_QUEUE_END >> 16) * 256]; 15662306a36Sopenharmony_ci} __cvmx_cmd_queue_all_state_t; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/** 15962306a36Sopenharmony_ci * Initialize a command queue for use. The initial FPA buffer is 16062306a36Sopenharmony_ci * allocated and the hardware unit is configured to point to the 16162306a36Sopenharmony_ci * new command queue. 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * @queue_id: Hardware command queue to initialize. 16462306a36Sopenharmony_ci * @max_depth: Maximum outstanding commands that can be queued. 16562306a36Sopenharmony_ci * @fpa_pool: FPA pool the command queues should come from. 16662306a36Sopenharmony_ci * @pool_size: Size of each buffer in the FPA pool (bytes) 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_cicvmx_cmd_queue_result_t cvmx_cmd_queue_initialize(cvmx_cmd_queue_id_t queue_id, 17162306a36Sopenharmony_ci int max_depth, int fpa_pool, 17262306a36Sopenharmony_ci int pool_size); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * Shutdown a queue a free it's command buffers to the FPA. The 17662306a36Sopenharmony_ci * hardware connected to the queue must be stopped before this 17762306a36Sopenharmony_ci * function is called. 17862306a36Sopenharmony_ci * 17962306a36Sopenharmony_ci * @queue_id: Queue to shutdown 18062306a36Sopenharmony_ci * 18162306a36Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cicvmx_cmd_queue_result_t cvmx_cmd_queue_shutdown(cvmx_cmd_queue_id_t queue_id); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/** 18662306a36Sopenharmony_ci * Return the number of command words pending in the queue. This 18762306a36Sopenharmony_ci * function may be relatively slow for some hardware units. 18862306a36Sopenharmony_ci * 18962306a36Sopenharmony_ci * @queue_id: Hardware command queue to query 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * Returns Number of outstanding commands 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ciint cvmx_cmd_queue_length(cvmx_cmd_queue_id_t queue_id); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/** 19662306a36Sopenharmony_ci * Return the command buffer to be written to. The purpose of this 19762306a36Sopenharmony_ci * function is to allow CVMX routine access t othe low level buffer 19862306a36Sopenharmony_ci * for initial hardware setup. User applications should not call this 19962306a36Sopenharmony_ci * function directly. 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * @queue_id: Command queue to query 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * Returns Command buffer or NULL on failure 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_civoid *cvmx_cmd_queue_buffer(cvmx_cmd_queue_id_t queue_id); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/** 20862306a36Sopenharmony_ci * Get the index into the state arrays for the supplied queue id. 20962306a36Sopenharmony_ci * 21062306a36Sopenharmony_ci * @queue_id: Queue ID to get an index for 21162306a36Sopenharmony_ci * 21262306a36Sopenharmony_ci * Returns Index into the state arrays 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_cistatic inline int __cvmx_cmd_queue_get_index(cvmx_cmd_queue_id_t queue_id) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * Warning: This code currently only works with devices that 21862306a36Sopenharmony_ci * have 256 queues or less. Devices with more than 16 queues 21962306a36Sopenharmony_ci * are laid out in memory to allow cores quick access to 22062306a36Sopenharmony_ci * every 16th queue. This reduces cache thrashing when you are 22162306a36Sopenharmony_ci * running 16 queues per port to support lockless operation. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci int unit = queue_id >> 16; 22462306a36Sopenharmony_ci int q = (queue_id >> 4) & 0xf; 22562306a36Sopenharmony_ci int core = queue_id & 0xf; 22662306a36Sopenharmony_ci return unit * 256 + core * 16 + q; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/** 23062306a36Sopenharmony_ci * Lock the supplied queue so nobody else is updating it at the same 23162306a36Sopenharmony_ci * time as us. 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * @queue_id: Queue ID to lock 23462306a36Sopenharmony_ci * @qptr: Pointer to the queue's global state 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_cistatic inline void __cvmx_cmd_queue_lock(cvmx_cmd_queue_id_t queue_id, 23762306a36Sopenharmony_ci __cvmx_cmd_queue_state_t *qptr) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci extern __cvmx_cmd_queue_all_state_t 24062306a36Sopenharmony_ci *__cvmx_cmd_queue_state_ptr; 24162306a36Sopenharmony_ci int tmp; 24262306a36Sopenharmony_ci int my_ticket; 24362306a36Sopenharmony_ci prefetch(qptr); 24462306a36Sopenharmony_ci asm volatile ( 24562306a36Sopenharmony_ci ".set push\n" 24662306a36Sopenharmony_ci ".set noreorder\n" 24762306a36Sopenharmony_ci "1:\n" 24862306a36Sopenharmony_ci /* Atomic add one to ticket_ptr */ 24962306a36Sopenharmony_ci "ll %[my_ticket], %[ticket_ptr]\n" 25062306a36Sopenharmony_ci /* and store the original value */ 25162306a36Sopenharmony_ci "li %[ticket], 1\n" 25262306a36Sopenharmony_ci /* in my_ticket */ 25362306a36Sopenharmony_ci "baddu %[ticket], %[my_ticket]\n" 25462306a36Sopenharmony_ci "sc %[ticket], %[ticket_ptr]\n" 25562306a36Sopenharmony_ci "beqz %[ticket], 1b\n" 25662306a36Sopenharmony_ci " nop\n" 25762306a36Sopenharmony_ci /* Load the current now_serving ticket */ 25862306a36Sopenharmony_ci "lbu %[ticket], %[now_serving]\n" 25962306a36Sopenharmony_ci "2:\n" 26062306a36Sopenharmony_ci /* Jump out if now_serving == my_ticket */ 26162306a36Sopenharmony_ci "beq %[ticket], %[my_ticket], 4f\n" 26262306a36Sopenharmony_ci /* Find out how many tickets are in front of me */ 26362306a36Sopenharmony_ci " subu %[ticket], %[my_ticket], %[ticket]\n" 26462306a36Sopenharmony_ci /* Use tickets in front of me minus one to delay */ 26562306a36Sopenharmony_ci "subu %[ticket], 1\n" 26662306a36Sopenharmony_ci /* Delay will be ((tickets in front)-1)*32 loops */ 26762306a36Sopenharmony_ci "cins %[ticket], %[ticket], 5, 7\n" 26862306a36Sopenharmony_ci "3:\n" 26962306a36Sopenharmony_ci /* Loop here until our ticket might be up */ 27062306a36Sopenharmony_ci "bnez %[ticket], 3b\n" 27162306a36Sopenharmony_ci " subu %[ticket], 1\n" 27262306a36Sopenharmony_ci /* Jump back up to check out ticket again */ 27362306a36Sopenharmony_ci "b 2b\n" 27462306a36Sopenharmony_ci /* Load the current now_serving ticket */ 27562306a36Sopenharmony_ci " lbu %[ticket], %[now_serving]\n" 27662306a36Sopenharmony_ci "4:\n" 27762306a36Sopenharmony_ci ".set pop\n" : 27862306a36Sopenharmony_ci [ticket_ptr] "=" GCC_OFF_SMALL_ASM()(__cvmx_cmd_queue_state_ptr->ticket[__cvmx_cmd_queue_get_index(queue_id)]), 27962306a36Sopenharmony_ci [now_serving] "=m"(qptr->now_serving), [ticket] "=r"(tmp), 28062306a36Sopenharmony_ci [my_ticket] "=r"(my_ticket) 28162306a36Sopenharmony_ci ); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/** 28562306a36Sopenharmony_ci * Unlock the queue, flushing all writes. 28662306a36Sopenharmony_ci * 28762306a36Sopenharmony_ci * @qptr: Queue to unlock 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_cistatic inline void __cvmx_cmd_queue_unlock(__cvmx_cmd_queue_state_t *qptr) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci qptr->now_serving++; 29262306a36Sopenharmony_ci CVMX_SYNCWS; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/** 29662306a36Sopenharmony_ci * Get the queue state structure for the given queue id 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * @queue_id: Queue id to get 29962306a36Sopenharmony_ci * 30062306a36Sopenharmony_ci * Returns Queue structure or NULL on failure 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_cistatic inline __cvmx_cmd_queue_state_t 30362306a36Sopenharmony_ci *__cvmx_cmd_queue_get_state(cvmx_cmd_queue_id_t queue_id) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci extern __cvmx_cmd_queue_all_state_t 30662306a36Sopenharmony_ci *__cvmx_cmd_queue_state_ptr; 30762306a36Sopenharmony_ci return &__cvmx_cmd_queue_state_ptr-> 30862306a36Sopenharmony_ci state[__cvmx_cmd_queue_get_index(queue_id)]; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci/** 31262306a36Sopenharmony_ci * Write an arbitrary number of command words to a command queue. 31362306a36Sopenharmony_ci * This is a generic function; the fixed number of command word 31462306a36Sopenharmony_ci * functions yield higher performance. 31562306a36Sopenharmony_ci * 31662306a36Sopenharmony_ci * @queue_id: Hardware command queue to write to 31762306a36Sopenharmony_ci * @use_locking: 31862306a36Sopenharmony_ci * Use internal locking to ensure exclusive access for queue 31962306a36Sopenharmony_ci * updates. If you don't use this locking you must ensure 32062306a36Sopenharmony_ci * exclusivity some other way. Locking is strongly recommended. 32162306a36Sopenharmony_ci * @cmd_count: Number of command words to write 32262306a36Sopenharmony_ci * @cmds: Array of commands to write 32362306a36Sopenharmony_ci * 32462306a36Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_cistatic inline cvmx_cmd_queue_result_t cvmx_cmd_queue_write(cvmx_cmd_queue_id_t 32762306a36Sopenharmony_ci queue_id, 32862306a36Sopenharmony_ci int use_locking, 32962306a36Sopenharmony_ci int cmd_count, 33062306a36Sopenharmony_ci uint64_t *cmds) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Make sure nobody else is updating the same queue */ 33562306a36Sopenharmony_ci if (likely(use_locking)) 33662306a36Sopenharmony_ci __cvmx_cmd_queue_lock(queue_id, qptr); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* 33962306a36Sopenharmony_ci * If a max queue length was specified then make sure we don't 34062306a36Sopenharmony_ci * exceed it. If any part of the command would be below the 34162306a36Sopenharmony_ci * limit we allow it. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_ci if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH && unlikely(qptr->max_depth)) { 34462306a36Sopenharmony_ci if (unlikely 34562306a36Sopenharmony_ci (cvmx_cmd_queue_length(queue_id) > (int)qptr->max_depth)) { 34662306a36Sopenharmony_ci if (likely(use_locking)) 34762306a36Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 34862306a36Sopenharmony_ci return CVMX_CMD_QUEUE_FULL; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* 35362306a36Sopenharmony_ci * Normally there is plenty of room in the current buffer for 35462306a36Sopenharmony_ci * the command. 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci if (likely(qptr->index + cmd_count < qptr->pool_size_m1)) { 35762306a36Sopenharmony_ci uint64_t *ptr = 35862306a36Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 35962306a36Sopenharmony_ci base_ptr_div128 << 7); 36062306a36Sopenharmony_ci ptr += qptr->index; 36162306a36Sopenharmony_ci qptr->index += cmd_count; 36262306a36Sopenharmony_ci while (cmd_count--) 36362306a36Sopenharmony_ci *ptr++ = *cmds++; 36462306a36Sopenharmony_ci } else { 36562306a36Sopenharmony_ci uint64_t *ptr; 36662306a36Sopenharmony_ci int count; 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * We need a new command buffer. Fail if there isn't 36962306a36Sopenharmony_ci * one available. 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci uint64_t *new_buffer = 37262306a36Sopenharmony_ci (uint64_t *) cvmx_fpa_alloc(qptr->fpa_pool); 37362306a36Sopenharmony_ci if (unlikely(new_buffer == NULL)) { 37462306a36Sopenharmony_ci if (likely(use_locking)) 37562306a36Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 37662306a36Sopenharmony_ci return CVMX_CMD_QUEUE_NO_MEMORY; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci ptr = 37962306a36Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 38062306a36Sopenharmony_ci base_ptr_div128 << 7); 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * Figure out how many command words will fit in this 38362306a36Sopenharmony_ci * buffer. One location will be needed for the next 38462306a36Sopenharmony_ci * buffer pointer. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ci count = qptr->pool_size_m1 - qptr->index; 38762306a36Sopenharmony_ci ptr += qptr->index; 38862306a36Sopenharmony_ci cmd_count -= count; 38962306a36Sopenharmony_ci while (count--) 39062306a36Sopenharmony_ci *ptr++ = *cmds++; 39162306a36Sopenharmony_ci *ptr = cvmx_ptr_to_phys(new_buffer); 39262306a36Sopenharmony_ci /* 39362306a36Sopenharmony_ci * The current buffer is full and has a link to the 39462306a36Sopenharmony_ci * next buffer. Time to write the rest of the commands 39562306a36Sopenharmony_ci * into the new buffer. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci qptr->base_ptr_div128 = *ptr >> 7; 39862306a36Sopenharmony_ci qptr->index = cmd_count; 39962306a36Sopenharmony_ci ptr = new_buffer; 40062306a36Sopenharmony_ci while (cmd_count--) 40162306a36Sopenharmony_ci *ptr++ = *cmds++; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* All updates are complete. Release the lock and return */ 40562306a36Sopenharmony_ci if (likely(use_locking)) 40662306a36Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 40762306a36Sopenharmony_ci return CVMX_CMD_QUEUE_SUCCESS; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/** 41162306a36Sopenharmony_ci * Simple function to write two command words to a command 41262306a36Sopenharmony_ci * queue. 41362306a36Sopenharmony_ci * 41462306a36Sopenharmony_ci * @queue_id: Hardware command queue to write to 41562306a36Sopenharmony_ci * @use_locking: 41662306a36Sopenharmony_ci * Use internal locking to ensure exclusive access for queue 41762306a36Sopenharmony_ci * updates. If you don't use this locking you must ensure 41862306a36Sopenharmony_ci * exclusivity some other way. Locking is strongly recommended. 41962306a36Sopenharmony_ci * @cmd1: Command 42062306a36Sopenharmony_ci * @cmd2: Command 42162306a36Sopenharmony_ci * 42262306a36Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_cistatic inline cvmx_cmd_queue_result_t cvmx_cmd_queue_write2(cvmx_cmd_queue_id_t 42562306a36Sopenharmony_ci queue_id, 42662306a36Sopenharmony_ci int use_locking, 42762306a36Sopenharmony_ci uint64_t cmd1, 42862306a36Sopenharmony_ci uint64_t cmd2) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Make sure nobody else is updating the same queue */ 43362306a36Sopenharmony_ci if (likely(use_locking)) 43462306a36Sopenharmony_ci __cvmx_cmd_queue_lock(queue_id, qptr); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * If a max queue length was specified then make sure we don't 43862306a36Sopenharmony_ci * exceed it. If any part of the command would be below the 43962306a36Sopenharmony_ci * limit we allow it. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_ci if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH && unlikely(qptr->max_depth)) { 44262306a36Sopenharmony_ci if (unlikely 44362306a36Sopenharmony_ci (cvmx_cmd_queue_length(queue_id) > (int)qptr->max_depth)) { 44462306a36Sopenharmony_ci if (likely(use_locking)) 44562306a36Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 44662306a36Sopenharmony_ci return CVMX_CMD_QUEUE_FULL; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* 45162306a36Sopenharmony_ci * Normally there is plenty of room in the current buffer for 45262306a36Sopenharmony_ci * the command. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci if (likely(qptr->index + 2 < qptr->pool_size_m1)) { 45562306a36Sopenharmony_ci uint64_t *ptr = 45662306a36Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 45762306a36Sopenharmony_ci base_ptr_div128 << 7); 45862306a36Sopenharmony_ci ptr += qptr->index; 45962306a36Sopenharmony_ci qptr->index += 2; 46062306a36Sopenharmony_ci ptr[0] = cmd1; 46162306a36Sopenharmony_ci ptr[1] = cmd2; 46262306a36Sopenharmony_ci } else { 46362306a36Sopenharmony_ci uint64_t *ptr; 46462306a36Sopenharmony_ci /* 46562306a36Sopenharmony_ci * Figure out how many command words will fit in this 46662306a36Sopenharmony_ci * buffer. One location will be needed for the next 46762306a36Sopenharmony_ci * buffer pointer. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci int count = qptr->pool_size_m1 - qptr->index; 47062306a36Sopenharmony_ci /* 47162306a36Sopenharmony_ci * We need a new command buffer. Fail if there isn't 47262306a36Sopenharmony_ci * one available. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_ci uint64_t *new_buffer = 47562306a36Sopenharmony_ci (uint64_t *) cvmx_fpa_alloc(qptr->fpa_pool); 47662306a36Sopenharmony_ci if (unlikely(new_buffer == NULL)) { 47762306a36Sopenharmony_ci if (likely(use_locking)) 47862306a36Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 47962306a36Sopenharmony_ci return CVMX_CMD_QUEUE_NO_MEMORY; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci count--; 48262306a36Sopenharmony_ci ptr = 48362306a36Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 48462306a36Sopenharmony_ci base_ptr_div128 << 7); 48562306a36Sopenharmony_ci ptr += qptr->index; 48662306a36Sopenharmony_ci *ptr++ = cmd1; 48762306a36Sopenharmony_ci if (likely(count)) 48862306a36Sopenharmony_ci *ptr++ = cmd2; 48962306a36Sopenharmony_ci *ptr = cvmx_ptr_to_phys(new_buffer); 49062306a36Sopenharmony_ci /* 49162306a36Sopenharmony_ci * The current buffer is full and has a link to the 49262306a36Sopenharmony_ci * next buffer. Time to write the rest of the commands 49362306a36Sopenharmony_ci * into the new buffer. 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_ci qptr->base_ptr_div128 = *ptr >> 7; 49662306a36Sopenharmony_ci qptr->index = 0; 49762306a36Sopenharmony_ci if (unlikely(count == 0)) { 49862306a36Sopenharmony_ci qptr->index = 1; 49962306a36Sopenharmony_ci new_buffer[0] = cmd2; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* All updates are complete. Release the lock and return */ 50462306a36Sopenharmony_ci if (likely(use_locking)) 50562306a36Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 50662306a36Sopenharmony_ci return CVMX_CMD_QUEUE_SUCCESS; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci/** 51062306a36Sopenharmony_ci * Simple function to write three command words to a command 51162306a36Sopenharmony_ci * queue. 51262306a36Sopenharmony_ci * 51362306a36Sopenharmony_ci * @queue_id: Hardware command queue to write to 51462306a36Sopenharmony_ci * @use_locking: 51562306a36Sopenharmony_ci * Use internal locking to ensure exclusive access for queue 51662306a36Sopenharmony_ci * updates. If you don't use this locking you must ensure 51762306a36Sopenharmony_ci * exclusivity some other way. Locking is strongly recommended. 51862306a36Sopenharmony_ci * @cmd1: Command 51962306a36Sopenharmony_ci * @cmd2: Command 52062306a36Sopenharmony_ci * @cmd3: Command 52162306a36Sopenharmony_ci * 52262306a36Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_cistatic inline cvmx_cmd_queue_result_t cvmx_cmd_queue_write3(cvmx_cmd_queue_id_t 52562306a36Sopenharmony_ci queue_id, 52662306a36Sopenharmony_ci int use_locking, 52762306a36Sopenharmony_ci uint64_t cmd1, 52862306a36Sopenharmony_ci uint64_t cmd2, 52962306a36Sopenharmony_ci uint64_t cmd3) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* Make sure nobody else is updating the same queue */ 53462306a36Sopenharmony_ci if (likely(use_locking)) 53562306a36Sopenharmony_ci __cvmx_cmd_queue_lock(queue_id, qptr); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* 53862306a36Sopenharmony_ci * If a max queue length was specified then make sure we don't 53962306a36Sopenharmony_ci * exceed it. If any part of the command would be below the 54062306a36Sopenharmony_ci * limit we allow it. 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_ci if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH && unlikely(qptr->max_depth)) { 54362306a36Sopenharmony_ci if (unlikely 54462306a36Sopenharmony_ci (cvmx_cmd_queue_length(queue_id) > (int)qptr->max_depth)) { 54562306a36Sopenharmony_ci if (likely(use_locking)) 54662306a36Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 54762306a36Sopenharmony_ci return CVMX_CMD_QUEUE_FULL; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* 55262306a36Sopenharmony_ci * Normally there is plenty of room in the current buffer for 55362306a36Sopenharmony_ci * the command. 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_ci if (likely(qptr->index + 3 < qptr->pool_size_m1)) { 55662306a36Sopenharmony_ci uint64_t *ptr = 55762306a36Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 55862306a36Sopenharmony_ci base_ptr_div128 << 7); 55962306a36Sopenharmony_ci ptr += qptr->index; 56062306a36Sopenharmony_ci qptr->index += 3; 56162306a36Sopenharmony_ci ptr[0] = cmd1; 56262306a36Sopenharmony_ci ptr[1] = cmd2; 56362306a36Sopenharmony_ci ptr[2] = cmd3; 56462306a36Sopenharmony_ci } else { 56562306a36Sopenharmony_ci uint64_t *ptr; 56662306a36Sopenharmony_ci /* 56762306a36Sopenharmony_ci * Figure out how many command words will fit in this 56862306a36Sopenharmony_ci * buffer. One location will be needed for the next 56962306a36Sopenharmony_ci * buffer pointer 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ci int count = qptr->pool_size_m1 - qptr->index; 57262306a36Sopenharmony_ci /* 57362306a36Sopenharmony_ci * We need a new command buffer. Fail if there isn't 57462306a36Sopenharmony_ci * one available 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci uint64_t *new_buffer = 57762306a36Sopenharmony_ci (uint64_t *) cvmx_fpa_alloc(qptr->fpa_pool); 57862306a36Sopenharmony_ci if (unlikely(new_buffer == NULL)) { 57962306a36Sopenharmony_ci if (likely(use_locking)) 58062306a36Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 58162306a36Sopenharmony_ci return CVMX_CMD_QUEUE_NO_MEMORY; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci count--; 58462306a36Sopenharmony_ci ptr = 58562306a36Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 58662306a36Sopenharmony_ci base_ptr_div128 << 7); 58762306a36Sopenharmony_ci ptr += qptr->index; 58862306a36Sopenharmony_ci *ptr++ = cmd1; 58962306a36Sopenharmony_ci if (count) { 59062306a36Sopenharmony_ci *ptr++ = cmd2; 59162306a36Sopenharmony_ci if (count > 1) 59262306a36Sopenharmony_ci *ptr++ = cmd3; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci *ptr = cvmx_ptr_to_phys(new_buffer); 59562306a36Sopenharmony_ci /* 59662306a36Sopenharmony_ci * The current buffer is full and has a link to the 59762306a36Sopenharmony_ci * next buffer. Time to write the rest of the commands 59862306a36Sopenharmony_ci * into the new buffer. 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci qptr->base_ptr_div128 = *ptr >> 7; 60162306a36Sopenharmony_ci qptr->index = 0; 60262306a36Sopenharmony_ci ptr = new_buffer; 60362306a36Sopenharmony_ci if (count == 0) { 60462306a36Sopenharmony_ci *ptr++ = cmd2; 60562306a36Sopenharmony_ci qptr->index++; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci if (count < 2) { 60862306a36Sopenharmony_ci *ptr++ = cmd3; 60962306a36Sopenharmony_ci qptr->index++; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* All updates are complete. Release the lock and return */ 61462306a36Sopenharmony_ci if (likely(use_locking)) 61562306a36Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 61662306a36Sopenharmony_ci return CVMX_CMD_QUEUE_SUCCESS; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci#endif /* __CVMX_CMD_QUEUE_H__ */ 620