18c2ecf20Sopenharmony_ci/***********************license start*************** 28c2ecf20Sopenharmony_ci * Author: Cavium Networks 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Contact: support@caviumnetworks.com 58c2ecf20Sopenharmony_ci * This file is part of the OCTEON SDK 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2003-2008 Cavium Networks 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This file is free software; you can redistribute it and/or modify 108c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License, Version 2, as 118c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This file is distributed in the hope that it will be useful, but 148c2ecf20Sopenharmony_ci * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty 158c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or 168c2ecf20Sopenharmony_ci * NONINFRINGEMENT. See the GNU General Public License for more 178c2ecf20Sopenharmony_ci * details. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License 208c2ecf20Sopenharmony_ci * along with this file; if not, write to the Free Software 218c2ecf20Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 228c2ecf20Sopenharmony_ci * or visit http://www.gnu.org/licenses/. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * This file may also be available under a different license from Cavium. 258c2ecf20Sopenharmony_ci * Contact Cavium Networks for more information 268c2ecf20Sopenharmony_ci ***********************license end**************************************/ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Support functions for managing command queues used for 318c2ecf20Sopenharmony_ci * various hardware blocks. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * The common command queue infrastructure abstracts out the 348c2ecf20Sopenharmony_ci * software necessary for adding to Octeon's chained queue 358c2ecf20Sopenharmony_ci * structures. These structures are used for commands to the 368c2ecf20Sopenharmony_ci * PKO, ZIP, DFA, RAID, and DMA engine blocks. Although each 378c2ecf20Sopenharmony_ci * hardware unit takes commands and CSRs of different types, 388c2ecf20Sopenharmony_ci * they all use basic linked command buffers to store the 398c2ecf20Sopenharmony_ci * pending request. In general, users of the CVMX API don't 408c2ecf20Sopenharmony_ci * call cvmx-cmd-queue functions directly. Instead the hardware 418c2ecf20Sopenharmony_ci * unit specific wrapper should be used. The wrappers perform 428c2ecf20Sopenharmony_ci * unit specific validation and CSR writes to submit the 438c2ecf20Sopenharmony_ci * commands. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * Even though most software will never directly interact with 468c2ecf20Sopenharmony_ci * cvmx-cmd-queue, knowledge of its internal working can help 478c2ecf20Sopenharmony_ci * in diagnosing performance problems and help with debugging. 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * Command queue pointers are stored in a global named block 508c2ecf20Sopenharmony_ci * called "cvmx_cmd_queues". Except for the PKO queues, each 518c2ecf20Sopenharmony_ci * hardware queue is stored in its own cache line to reduce SMP 528c2ecf20Sopenharmony_ci * contention on spin locks. The PKO queues are stored such that 538c2ecf20Sopenharmony_ci * every 16th queue is next to each other in memory. This scheme 548c2ecf20Sopenharmony_ci * allows for queues being in separate cache lines when there 558c2ecf20Sopenharmony_ci * are low number of queues per port. With 16 queues per port, 568c2ecf20Sopenharmony_ci * the first queue for each port is in the same cache area. The 578c2ecf20Sopenharmony_ci * second queues for each port are in another area, etc. This 588c2ecf20Sopenharmony_ci * allows software to implement very efficient lockless PKO with 598c2ecf20Sopenharmony_ci * 16 queues per port using a minimum of cache lines per core. 608c2ecf20Sopenharmony_ci * All queues for a given core will be isolated in the same 618c2ecf20Sopenharmony_ci * cache area. 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * In addition to the memory pointer layout, cvmx-cmd-queue 648c2ecf20Sopenharmony_ci * provides an optimized fair ll/sc locking mechanism for the 658c2ecf20Sopenharmony_ci * queues. The lock uses a "ticket / now serving" model to 668c2ecf20Sopenharmony_ci * maintain fair order on contended locks. In addition, it uses 678c2ecf20Sopenharmony_ci * predicted locking time to limit cache contention. When a core 688c2ecf20Sopenharmony_ci * know it must wait in line for a lock, it spins on the 698c2ecf20Sopenharmony_ci * internal cycle counter to completely eliminate any causes of 708c2ecf20Sopenharmony_ci * bus traffic. 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#ifndef __CVMX_CMD_QUEUE_H__ 758c2ecf20Sopenharmony_ci#define __CVMX_CMD_QUEUE_H__ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#include <asm/compiler.h> 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#include <asm/octeon/cvmx-fpa.h> 828c2ecf20Sopenharmony_ci/** 838c2ecf20Sopenharmony_ci * By default we disable the max depth support. Most programs 848c2ecf20Sopenharmony_ci * don't use it and it slows down the command queue processing 858c2ecf20Sopenharmony_ci * significantly. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci#ifndef CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH 888c2ecf20Sopenharmony_ci#define CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH 0 898c2ecf20Sopenharmony_ci#endif 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/** 928c2ecf20Sopenharmony_ci * Enumeration representing all hardware blocks that use command 938c2ecf20Sopenharmony_ci * queues. Each hardware block has up to 65536 sub identifiers for 948c2ecf20Sopenharmony_ci * multiple command queues. Not all chips support all hardware 958c2ecf20Sopenharmony_ci * units. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_citypedef enum { 988c2ecf20Sopenharmony_ci CVMX_CMD_QUEUE_PKO_BASE = 0x00000, 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define CVMX_CMD_QUEUE_PKO(queue) \ 1018c2ecf20Sopenharmony_ci ((cvmx_cmd_queue_id_t)(CVMX_CMD_QUEUE_PKO_BASE + (0xffff&(queue)))) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci CVMX_CMD_QUEUE_ZIP = 0x10000, 1048c2ecf20Sopenharmony_ci CVMX_CMD_QUEUE_DFA = 0x20000, 1058c2ecf20Sopenharmony_ci CVMX_CMD_QUEUE_RAID = 0x30000, 1068c2ecf20Sopenharmony_ci CVMX_CMD_QUEUE_DMA_BASE = 0x40000, 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define CVMX_CMD_QUEUE_DMA(queue) \ 1098c2ecf20Sopenharmony_ci ((cvmx_cmd_queue_id_t)(CVMX_CMD_QUEUE_DMA_BASE + (0xffff&(queue)))) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci CVMX_CMD_QUEUE_END = 0x50000, 1128c2ecf20Sopenharmony_ci} cvmx_cmd_queue_id_t; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/** 1158c2ecf20Sopenharmony_ci * Command write operations can fail if the command queue needs 1168c2ecf20Sopenharmony_ci * a new buffer and the associated FPA pool is empty. It can also 1178c2ecf20Sopenharmony_ci * fail if the number of queued command words reaches the maximum 1188c2ecf20Sopenharmony_ci * set at initialization. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_citypedef enum { 1218c2ecf20Sopenharmony_ci CVMX_CMD_QUEUE_SUCCESS = 0, 1228c2ecf20Sopenharmony_ci CVMX_CMD_QUEUE_NO_MEMORY = -1, 1238c2ecf20Sopenharmony_ci CVMX_CMD_QUEUE_FULL = -2, 1248c2ecf20Sopenharmony_ci CVMX_CMD_QUEUE_INVALID_PARAM = -3, 1258c2ecf20Sopenharmony_ci CVMX_CMD_QUEUE_ALREADY_SETUP = -4, 1268c2ecf20Sopenharmony_ci} cvmx_cmd_queue_result_t; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_citypedef struct { 1298c2ecf20Sopenharmony_ci /* You have lock when this is your ticket */ 1308c2ecf20Sopenharmony_ci uint8_t now_serving; 1318c2ecf20Sopenharmony_ci uint64_t unused1:24; 1328c2ecf20Sopenharmony_ci /* Maximum outstanding command words */ 1338c2ecf20Sopenharmony_ci uint32_t max_depth; 1348c2ecf20Sopenharmony_ci /* FPA pool buffers come from */ 1358c2ecf20Sopenharmony_ci uint64_t fpa_pool:3; 1368c2ecf20Sopenharmony_ci /* Top of command buffer pointer shifted 7 */ 1378c2ecf20Sopenharmony_ci uint64_t base_ptr_div128:29; 1388c2ecf20Sopenharmony_ci uint64_t unused2:6; 1398c2ecf20Sopenharmony_ci /* FPA buffer size in 64bit words minus 1 */ 1408c2ecf20Sopenharmony_ci uint64_t pool_size_m1:13; 1418c2ecf20Sopenharmony_ci /* Number of commands already used in buffer */ 1428c2ecf20Sopenharmony_ci uint64_t index:13; 1438c2ecf20Sopenharmony_ci} __cvmx_cmd_queue_state_t; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/** 1468c2ecf20Sopenharmony_ci * This structure contains the global state of all command queues. 1478c2ecf20Sopenharmony_ci * It is stored in a bootmem named block and shared by all 1488c2ecf20Sopenharmony_ci * applications running on Octeon. Tickets are stored in a differnet 1498c2ecf20Sopenharmony_ci * cache line that queue information to reduce the contention on the 1508c2ecf20Sopenharmony_ci * ll/sc used to get a ticket. If this is not the case, the update 1518c2ecf20Sopenharmony_ci * of queue state causes the ll/sc to fail quite often. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_citypedef struct { 1548c2ecf20Sopenharmony_ci uint64_t ticket[(CVMX_CMD_QUEUE_END >> 16) * 256]; 1558c2ecf20Sopenharmony_ci __cvmx_cmd_queue_state_t state[(CVMX_CMD_QUEUE_END >> 16) * 256]; 1568c2ecf20Sopenharmony_ci} __cvmx_cmd_queue_all_state_t; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/** 1598c2ecf20Sopenharmony_ci * Initialize a command queue for use. The initial FPA buffer is 1608c2ecf20Sopenharmony_ci * allocated and the hardware unit is configured to point to the 1618c2ecf20Sopenharmony_ci * new command queue. 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * @queue_id: Hardware command queue to initialize. 1648c2ecf20Sopenharmony_ci * @max_depth: Maximum outstanding commands that can be queued. 1658c2ecf20Sopenharmony_ci * @fpa_pool: FPA pool the command queues should come from. 1668c2ecf20Sopenharmony_ci * @pool_size: Size of each buffer in the FPA pool (bytes) 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_cicvmx_cmd_queue_result_t cvmx_cmd_queue_initialize(cvmx_cmd_queue_id_t queue_id, 1718c2ecf20Sopenharmony_ci int max_depth, int fpa_pool, 1728c2ecf20Sopenharmony_ci int pool_size); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/** 1758c2ecf20Sopenharmony_ci * Shutdown a queue a free it's command buffers to the FPA. The 1768c2ecf20Sopenharmony_ci * hardware connected to the queue must be stopped before this 1778c2ecf20Sopenharmony_ci * function is called. 1788c2ecf20Sopenharmony_ci * 1798c2ecf20Sopenharmony_ci * @queue_id: Queue to shutdown 1808c2ecf20Sopenharmony_ci * 1818c2ecf20Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_cicvmx_cmd_queue_result_t cvmx_cmd_queue_shutdown(cvmx_cmd_queue_id_t queue_id); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/** 1868c2ecf20Sopenharmony_ci * Return the number of command words pending in the queue. This 1878c2ecf20Sopenharmony_ci * function may be relatively slow for some hardware units. 1888c2ecf20Sopenharmony_ci * 1898c2ecf20Sopenharmony_ci * @queue_id: Hardware command queue to query 1908c2ecf20Sopenharmony_ci * 1918c2ecf20Sopenharmony_ci * Returns Number of outstanding commands 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ciint cvmx_cmd_queue_length(cvmx_cmd_queue_id_t queue_id); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/** 1968c2ecf20Sopenharmony_ci * Return the command buffer to be written to. The purpose of this 1978c2ecf20Sopenharmony_ci * function is to allow CVMX routine access t othe low level buffer 1988c2ecf20Sopenharmony_ci * for initial hardware setup. User applications should not call this 1998c2ecf20Sopenharmony_ci * function directly. 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * @queue_id: Command queue to query 2028c2ecf20Sopenharmony_ci * 2038c2ecf20Sopenharmony_ci * Returns Command buffer or NULL on failure 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_civoid *cvmx_cmd_queue_buffer(cvmx_cmd_queue_id_t queue_id); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/** 2088c2ecf20Sopenharmony_ci * Get the index into the state arrays for the supplied queue id. 2098c2ecf20Sopenharmony_ci * 2108c2ecf20Sopenharmony_ci * @queue_id: Queue ID to get an index for 2118c2ecf20Sopenharmony_ci * 2128c2ecf20Sopenharmony_ci * Returns Index into the state arrays 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_cistatic inline int __cvmx_cmd_queue_get_index(cvmx_cmd_queue_id_t queue_id) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * Warning: This code currently only works with devices that 2188c2ecf20Sopenharmony_ci * have 256 queues or less. Devices with more than 16 queues 2198c2ecf20Sopenharmony_ci * are laid out in memory to allow cores quick access to 2208c2ecf20Sopenharmony_ci * every 16th queue. This reduces cache thrashing when you are 2218c2ecf20Sopenharmony_ci * running 16 queues per port to support lockless operation. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci int unit = queue_id >> 16; 2248c2ecf20Sopenharmony_ci int q = (queue_id >> 4) & 0xf; 2258c2ecf20Sopenharmony_ci int core = queue_id & 0xf; 2268c2ecf20Sopenharmony_ci return unit * 256 + core * 16 + q; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/** 2308c2ecf20Sopenharmony_ci * Lock the supplied queue so nobody else is updating it at the same 2318c2ecf20Sopenharmony_ci * time as us. 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * @queue_id: Queue ID to lock 2348c2ecf20Sopenharmony_ci * @qptr: Pointer to the queue's global state 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_cistatic inline void __cvmx_cmd_queue_lock(cvmx_cmd_queue_id_t queue_id, 2378c2ecf20Sopenharmony_ci __cvmx_cmd_queue_state_t *qptr) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci extern __cvmx_cmd_queue_all_state_t 2408c2ecf20Sopenharmony_ci *__cvmx_cmd_queue_state_ptr; 2418c2ecf20Sopenharmony_ci int tmp; 2428c2ecf20Sopenharmony_ci int my_ticket; 2438c2ecf20Sopenharmony_ci prefetch(qptr); 2448c2ecf20Sopenharmony_ci asm volatile ( 2458c2ecf20Sopenharmony_ci ".set push\n" 2468c2ecf20Sopenharmony_ci ".set noreorder\n" 2478c2ecf20Sopenharmony_ci "1:\n" 2488c2ecf20Sopenharmony_ci /* Atomic add one to ticket_ptr */ 2498c2ecf20Sopenharmony_ci "ll %[my_ticket], %[ticket_ptr]\n" 2508c2ecf20Sopenharmony_ci /* and store the original value */ 2518c2ecf20Sopenharmony_ci "li %[ticket], 1\n" 2528c2ecf20Sopenharmony_ci /* in my_ticket */ 2538c2ecf20Sopenharmony_ci "baddu %[ticket], %[my_ticket]\n" 2548c2ecf20Sopenharmony_ci "sc %[ticket], %[ticket_ptr]\n" 2558c2ecf20Sopenharmony_ci "beqz %[ticket], 1b\n" 2568c2ecf20Sopenharmony_ci " nop\n" 2578c2ecf20Sopenharmony_ci /* Load the current now_serving ticket */ 2588c2ecf20Sopenharmony_ci "lbu %[ticket], %[now_serving]\n" 2598c2ecf20Sopenharmony_ci "2:\n" 2608c2ecf20Sopenharmony_ci /* Jump out if now_serving == my_ticket */ 2618c2ecf20Sopenharmony_ci "beq %[ticket], %[my_ticket], 4f\n" 2628c2ecf20Sopenharmony_ci /* Find out how many tickets are in front of me */ 2638c2ecf20Sopenharmony_ci " subu %[ticket], %[my_ticket], %[ticket]\n" 2648c2ecf20Sopenharmony_ci /* Use tickets in front of me minus one to delay */ 2658c2ecf20Sopenharmony_ci "subu %[ticket], 1\n" 2668c2ecf20Sopenharmony_ci /* Delay will be ((tickets in front)-1)*32 loops */ 2678c2ecf20Sopenharmony_ci "cins %[ticket], %[ticket], 5, 7\n" 2688c2ecf20Sopenharmony_ci "3:\n" 2698c2ecf20Sopenharmony_ci /* Loop here until our ticket might be up */ 2708c2ecf20Sopenharmony_ci "bnez %[ticket], 3b\n" 2718c2ecf20Sopenharmony_ci " subu %[ticket], 1\n" 2728c2ecf20Sopenharmony_ci /* Jump back up to check out ticket again */ 2738c2ecf20Sopenharmony_ci "b 2b\n" 2748c2ecf20Sopenharmony_ci /* Load the current now_serving ticket */ 2758c2ecf20Sopenharmony_ci " lbu %[ticket], %[now_serving]\n" 2768c2ecf20Sopenharmony_ci "4:\n" 2778c2ecf20Sopenharmony_ci ".set pop\n" : 2788c2ecf20Sopenharmony_ci [ticket_ptr] "=" GCC_OFF_SMALL_ASM()(__cvmx_cmd_queue_state_ptr->ticket[__cvmx_cmd_queue_get_index(queue_id)]), 2798c2ecf20Sopenharmony_ci [now_serving] "=m"(qptr->now_serving), [ticket] "=r"(tmp), 2808c2ecf20Sopenharmony_ci [my_ticket] "=r"(my_ticket) 2818c2ecf20Sopenharmony_ci ); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/** 2858c2ecf20Sopenharmony_ci * Unlock the queue, flushing all writes. 2868c2ecf20Sopenharmony_ci * 2878c2ecf20Sopenharmony_ci * @qptr: Queue to unlock 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_cistatic inline void __cvmx_cmd_queue_unlock(__cvmx_cmd_queue_state_t *qptr) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci qptr->now_serving++; 2928c2ecf20Sopenharmony_ci CVMX_SYNCWS; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/** 2968c2ecf20Sopenharmony_ci * Get the queue state structure for the given queue id 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * @queue_id: Queue id to get 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * Returns Queue structure or NULL on failure 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic inline __cvmx_cmd_queue_state_t 3038c2ecf20Sopenharmony_ci *__cvmx_cmd_queue_get_state(cvmx_cmd_queue_id_t queue_id) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci extern __cvmx_cmd_queue_all_state_t 3068c2ecf20Sopenharmony_ci *__cvmx_cmd_queue_state_ptr; 3078c2ecf20Sopenharmony_ci return &__cvmx_cmd_queue_state_ptr-> 3088c2ecf20Sopenharmony_ci state[__cvmx_cmd_queue_get_index(queue_id)]; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/** 3128c2ecf20Sopenharmony_ci * Write an arbitrary number of command words to a command queue. 3138c2ecf20Sopenharmony_ci * This is a generic function; the fixed number of command word 3148c2ecf20Sopenharmony_ci * functions yield higher performance. 3158c2ecf20Sopenharmony_ci * 3168c2ecf20Sopenharmony_ci * @queue_id: Hardware command queue to write to 3178c2ecf20Sopenharmony_ci * @use_locking: 3188c2ecf20Sopenharmony_ci * Use internal locking to ensure exclusive access for queue 3198c2ecf20Sopenharmony_ci * updates. If you don't use this locking you must ensure 3208c2ecf20Sopenharmony_ci * exclusivity some other way. Locking is strongly recommended. 3218c2ecf20Sopenharmony_ci * @cmd_count: Number of command words to write 3228c2ecf20Sopenharmony_ci * @cmds: Array of commands to write 3238c2ecf20Sopenharmony_ci * 3248c2ecf20Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_cistatic inline cvmx_cmd_queue_result_t cvmx_cmd_queue_write(cvmx_cmd_queue_id_t 3278c2ecf20Sopenharmony_ci queue_id, 3288c2ecf20Sopenharmony_ci int use_locking, 3298c2ecf20Sopenharmony_ci int cmd_count, 3308c2ecf20Sopenharmony_ci uint64_t *cmds) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* Make sure nobody else is updating the same queue */ 3358c2ecf20Sopenharmony_ci if (likely(use_locking)) 3368c2ecf20Sopenharmony_ci __cvmx_cmd_queue_lock(queue_id, qptr); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* 3398c2ecf20Sopenharmony_ci * If a max queue length was specified then make sure we don't 3408c2ecf20Sopenharmony_ci * exceed it. If any part of the command would be below the 3418c2ecf20Sopenharmony_ci * limit we allow it. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ci if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH && unlikely(qptr->max_depth)) { 3448c2ecf20Sopenharmony_ci if (unlikely 3458c2ecf20Sopenharmony_ci (cvmx_cmd_queue_length(queue_id) > (int)qptr->max_depth)) { 3468c2ecf20Sopenharmony_ci if (likely(use_locking)) 3478c2ecf20Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 3488c2ecf20Sopenharmony_ci return CVMX_CMD_QUEUE_FULL; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * Normally there is plenty of room in the current buffer for 3548c2ecf20Sopenharmony_ci * the command. 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_ci if (likely(qptr->index + cmd_count < qptr->pool_size_m1)) { 3578c2ecf20Sopenharmony_ci uint64_t *ptr = 3588c2ecf20Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 3598c2ecf20Sopenharmony_ci base_ptr_div128 << 7); 3608c2ecf20Sopenharmony_ci ptr += qptr->index; 3618c2ecf20Sopenharmony_ci qptr->index += cmd_count; 3628c2ecf20Sopenharmony_ci while (cmd_count--) 3638c2ecf20Sopenharmony_ci *ptr++ = *cmds++; 3648c2ecf20Sopenharmony_ci } else { 3658c2ecf20Sopenharmony_ci uint64_t *ptr; 3668c2ecf20Sopenharmony_ci int count; 3678c2ecf20Sopenharmony_ci /* 3688c2ecf20Sopenharmony_ci * We need a new command buffer. Fail if there isn't 3698c2ecf20Sopenharmony_ci * one available. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci uint64_t *new_buffer = 3728c2ecf20Sopenharmony_ci (uint64_t *) cvmx_fpa_alloc(qptr->fpa_pool); 3738c2ecf20Sopenharmony_ci if (unlikely(new_buffer == NULL)) { 3748c2ecf20Sopenharmony_ci if (likely(use_locking)) 3758c2ecf20Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 3768c2ecf20Sopenharmony_ci return CVMX_CMD_QUEUE_NO_MEMORY; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci ptr = 3798c2ecf20Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 3808c2ecf20Sopenharmony_ci base_ptr_div128 << 7); 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * Figure out how many command words will fit in this 3838c2ecf20Sopenharmony_ci * buffer. One location will be needed for the next 3848c2ecf20Sopenharmony_ci * buffer pointer. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci count = qptr->pool_size_m1 - qptr->index; 3878c2ecf20Sopenharmony_ci ptr += qptr->index; 3888c2ecf20Sopenharmony_ci cmd_count -= count; 3898c2ecf20Sopenharmony_ci while (count--) 3908c2ecf20Sopenharmony_ci *ptr++ = *cmds++; 3918c2ecf20Sopenharmony_ci *ptr = cvmx_ptr_to_phys(new_buffer); 3928c2ecf20Sopenharmony_ci /* 3938c2ecf20Sopenharmony_ci * The current buffer is full and has a link to the 3948c2ecf20Sopenharmony_ci * next buffer. Time to write the rest of the commands 3958c2ecf20Sopenharmony_ci * into the new buffer. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci qptr->base_ptr_div128 = *ptr >> 7; 3988c2ecf20Sopenharmony_ci qptr->index = cmd_count; 3998c2ecf20Sopenharmony_ci ptr = new_buffer; 4008c2ecf20Sopenharmony_ci while (cmd_count--) 4018c2ecf20Sopenharmony_ci *ptr++ = *cmds++; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* All updates are complete. Release the lock and return */ 4058c2ecf20Sopenharmony_ci if (likely(use_locking)) 4068c2ecf20Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 4078c2ecf20Sopenharmony_ci return CVMX_CMD_QUEUE_SUCCESS; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/** 4118c2ecf20Sopenharmony_ci * Simple function to write two command words to a command 4128c2ecf20Sopenharmony_ci * queue. 4138c2ecf20Sopenharmony_ci * 4148c2ecf20Sopenharmony_ci * @queue_id: Hardware command queue to write to 4158c2ecf20Sopenharmony_ci * @use_locking: 4168c2ecf20Sopenharmony_ci * Use internal locking to ensure exclusive access for queue 4178c2ecf20Sopenharmony_ci * updates. If you don't use this locking you must ensure 4188c2ecf20Sopenharmony_ci * exclusivity some other way. Locking is strongly recommended. 4198c2ecf20Sopenharmony_ci * @cmd1: Command 4208c2ecf20Sopenharmony_ci * @cmd2: Command 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_cistatic inline cvmx_cmd_queue_result_t cvmx_cmd_queue_write2(cvmx_cmd_queue_id_t 4258c2ecf20Sopenharmony_ci queue_id, 4268c2ecf20Sopenharmony_ci int use_locking, 4278c2ecf20Sopenharmony_ci uint64_t cmd1, 4288c2ecf20Sopenharmony_ci uint64_t cmd2) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Make sure nobody else is updating the same queue */ 4338c2ecf20Sopenharmony_ci if (likely(use_locking)) 4348c2ecf20Sopenharmony_ci __cvmx_cmd_queue_lock(queue_id, qptr); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* 4378c2ecf20Sopenharmony_ci * If a max queue length was specified then make sure we don't 4388c2ecf20Sopenharmony_ci * exceed it. If any part of the command would be below the 4398c2ecf20Sopenharmony_ci * limit we allow it. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH && unlikely(qptr->max_depth)) { 4428c2ecf20Sopenharmony_ci if (unlikely 4438c2ecf20Sopenharmony_ci (cvmx_cmd_queue_length(queue_id) > (int)qptr->max_depth)) { 4448c2ecf20Sopenharmony_ci if (likely(use_locking)) 4458c2ecf20Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 4468c2ecf20Sopenharmony_ci return CVMX_CMD_QUEUE_FULL; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* 4518c2ecf20Sopenharmony_ci * Normally there is plenty of room in the current buffer for 4528c2ecf20Sopenharmony_ci * the command. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci if (likely(qptr->index + 2 < qptr->pool_size_m1)) { 4558c2ecf20Sopenharmony_ci uint64_t *ptr = 4568c2ecf20Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 4578c2ecf20Sopenharmony_ci base_ptr_div128 << 7); 4588c2ecf20Sopenharmony_ci ptr += qptr->index; 4598c2ecf20Sopenharmony_ci qptr->index += 2; 4608c2ecf20Sopenharmony_ci ptr[0] = cmd1; 4618c2ecf20Sopenharmony_ci ptr[1] = cmd2; 4628c2ecf20Sopenharmony_ci } else { 4638c2ecf20Sopenharmony_ci uint64_t *ptr; 4648c2ecf20Sopenharmony_ci /* 4658c2ecf20Sopenharmony_ci * Figure out how many command words will fit in this 4668c2ecf20Sopenharmony_ci * buffer. One location will be needed for the next 4678c2ecf20Sopenharmony_ci * buffer pointer. 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_ci int count = qptr->pool_size_m1 - qptr->index; 4708c2ecf20Sopenharmony_ci /* 4718c2ecf20Sopenharmony_ci * We need a new command buffer. Fail if there isn't 4728c2ecf20Sopenharmony_ci * one available. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci uint64_t *new_buffer = 4758c2ecf20Sopenharmony_ci (uint64_t *) cvmx_fpa_alloc(qptr->fpa_pool); 4768c2ecf20Sopenharmony_ci if (unlikely(new_buffer == NULL)) { 4778c2ecf20Sopenharmony_ci if (likely(use_locking)) 4788c2ecf20Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 4798c2ecf20Sopenharmony_ci return CVMX_CMD_QUEUE_NO_MEMORY; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci count--; 4828c2ecf20Sopenharmony_ci ptr = 4838c2ecf20Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 4848c2ecf20Sopenharmony_ci base_ptr_div128 << 7); 4858c2ecf20Sopenharmony_ci ptr += qptr->index; 4868c2ecf20Sopenharmony_ci *ptr++ = cmd1; 4878c2ecf20Sopenharmony_ci if (likely(count)) 4888c2ecf20Sopenharmony_ci *ptr++ = cmd2; 4898c2ecf20Sopenharmony_ci *ptr = cvmx_ptr_to_phys(new_buffer); 4908c2ecf20Sopenharmony_ci /* 4918c2ecf20Sopenharmony_ci * The current buffer is full and has a link to the 4928c2ecf20Sopenharmony_ci * next buffer. Time to write the rest of the commands 4938c2ecf20Sopenharmony_ci * into the new buffer. 4948c2ecf20Sopenharmony_ci */ 4958c2ecf20Sopenharmony_ci qptr->base_ptr_div128 = *ptr >> 7; 4968c2ecf20Sopenharmony_ci qptr->index = 0; 4978c2ecf20Sopenharmony_ci if (unlikely(count == 0)) { 4988c2ecf20Sopenharmony_ci qptr->index = 1; 4998c2ecf20Sopenharmony_ci new_buffer[0] = cmd2; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* All updates are complete. Release the lock and return */ 5048c2ecf20Sopenharmony_ci if (likely(use_locking)) 5058c2ecf20Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 5068c2ecf20Sopenharmony_ci return CVMX_CMD_QUEUE_SUCCESS; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci/** 5108c2ecf20Sopenharmony_ci * Simple function to write three command words to a command 5118c2ecf20Sopenharmony_ci * queue. 5128c2ecf20Sopenharmony_ci * 5138c2ecf20Sopenharmony_ci * @queue_id: Hardware command queue to write to 5148c2ecf20Sopenharmony_ci * @use_locking: 5158c2ecf20Sopenharmony_ci * Use internal locking to ensure exclusive access for queue 5168c2ecf20Sopenharmony_ci * updates. If you don't use this locking you must ensure 5178c2ecf20Sopenharmony_ci * exclusivity some other way. Locking is strongly recommended. 5188c2ecf20Sopenharmony_ci * @cmd1: Command 5198c2ecf20Sopenharmony_ci * @cmd2: Command 5208c2ecf20Sopenharmony_ci * @cmd3: Command 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_cistatic inline cvmx_cmd_queue_result_t cvmx_cmd_queue_write3(cvmx_cmd_queue_id_t 5258c2ecf20Sopenharmony_ci queue_id, 5268c2ecf20Sopenharmony_ci int use_locking, 5278c2ecf20Sopenharmony_ci uint64_t cmd1, 5288c2ecf20Sopenharmony_ci uint64_t cmd2, 5298c2ecf20Sopenharmony_ci uint64_t cmd3) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* Make sure nobody else is updating the same queue */ 5348c2ecf20Sopenharmony_ci if (likely(use_locking)) 5358c2ecf20Sopenharmony_ci __cvmx_cmd_queue_lock(queue_id, qptr); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* 5388c2ecf20Sopenharmony_ci * If a max queue length was specified then make sure we don't 5398c2ecf20Sopenharmony_ci * exceed it. If any part of the command would be below the 5408c2ecf20Sopenharmony_ci * limit we allow it. 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH && unlikely(qptr->max_depth)) { 5438c2ecf20Sopenharmony_ci if (unlikely 5448c2ecf20Sopenharmony_ci (cvmx_cmd_queue_length(queue_id) > (int)qptr->max_depth)) { 5458c2ecf20Sopenharmony_ci if (likely(use_locking)) 5468c2ecf20Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 5478c2ecf20Sopenharmony_ci return CVMX_CMD_QUEUE_FULL; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* 5528c2ecf20Sopenharmony_ci * Normally there is plenty of room in the current buffer for 5538c2ecf20Sopenharmony_ci * the command. 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_ci if (likely(qptr->index + 3 < qptr->pool_size_m1)) { 5568c2ecf20Sopenharmony_ci uint64_t *ptr = 5578c2ecf20Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 5588c2ecf20Sopenharmony_ci base_ptr_div128 << 7); 5598c2ecf20Sopenharmony_ci ptr += qptr->index; 5608c2ecf20Sopenharmony_ci qptr->index += 3; 5618c2ecf20Sopenharmony_ci ptr[0] = cmd1; 5628c2ecf20Sopenharmony_ci ptr[1] = cmd2; 5638c2ecf20Sopenharmony_ci ptr[2] = cmd3; 5648c2ecf20Sopenharmony_ci } else { 5658c2ecf20Sopenharmony_ci uint64_t *ptr; 5668c2ecf20Sopenharmony_ci /* 5678c2ecf20Sopenharmony_ci * Figure out how many command words will fit in this 5688c2ecf20Sopenharmony_ci * buffer. One location will be needed for the next 5698c2ecf20Sopenharmony_ci * buffer pointer 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_ci int count = qptr->pool_size_m1 - qptr->index; 5728c2ecf20Sopenharmony_ci /* 5738c2ecf20Sopenharmony_ci * We need a new command buffer. Fail if there isn't 5748c2ecf20Sopenharmony_ci * one available 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_ci uint64_t *new_buffer = 5778c2ecf20Sopenharmony_ci (uint64_t *) cvmx_fpa_alloc(qptr->fpa_pool); 5788c2ecf20Sopenharmony_ci if (unlikely(new_buffer == NULL)) { 5798c2ecf20Sopenharmony_ci if (likely(use_locking)) 5808c2ecf20Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 5818c2ecf20Sopenharmony_ci return CVMX_CMD_QUEUE_NO_MEMORY; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci count--; 5848c2ecf20Sopenharmony_ci ptr = 5858c2ecf20Sopenharmony_ci (uint64_t *) cvmx_phys_to_ptr((uint64_t) qptr-> 5868c2ecf20Sopenharmony_ci base_ptr_div128 << 7); 5878c2ecf20Sopenharmony_ci ptr += qptr->index; 5888c2ecf20Sopenharmony_ci *ptr++ = cmd1; 5898c2ecf20Sopenharmony_ci if (count) { 5908c2ecf20Sopenharmony_ci *ptr++ = cmd2; 5918c2ecf20Sopenharmony_ci if (count > 1) 5928c2ecf20Sopenharmony_ci *ptr++ = cmd3; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci *ptr = cvmx_ptr_to_phys(new_buffer); 5958c2ecf20Sopenharmony_ci /* 5968c2ecf20Sopenharmony_ci * The current buffer is full and has a link to the 5978c2ecf20Sopenharmony_ci * next buffer. Time to write the rest of the commands 5988c2ecf20Sopenharmony_ci * into the new buffer. 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_ci qptr->base_ptr_div128 = *ptr >> 7; 6018c2ecf20Sopenharmony_ci qptr->index = 0; 6028c2ecf20Sopenharmony_ci ptr = new_buffer; 6038c2ecf20Sopenharmony_ci if (count == 0) { 6048c2ecf20Sopenharmony_ci *ptr++ = cmd2; 6058c2ecf20Sopenharmony_ci qptr->index++; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci if (count < 2) { 6088c2ecf20Sopenharmony_ci *ptr++ = cmd3; 6098c2ecf20Sopenharmony_ci qptr->index++; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* All updates are complete. Release the lock and return */ 6148c2ecf20Sopenharmony_ci if (likely(use_locking)) 6158c2ecf20Sopenharmony_ci __cvmx_cmd_queue_unlock(qptr); 6168c2ecf20Sopenharmony_ci return CVMX_CMD_QUEUE_SUCCESS; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci#endif /* __CVMX_CMD_QUEUE_H__ */ 620