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