~funderscore blog cgit wiki get in touch
aboutsummaryrefslogtreecommitdiff
blob: c4b49f940b628b08a689afd3993f94170c79b406 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2018-2022 Marvell International Ltd.
 *
 * Support functions for managing command queues used for
 * various hardware blocks.
 */

#include <errno.h>
#include <log.h>
#include <time.h>
#include <linux/delay.h>

#include <mach/cvmx-regs.h>
#include <mach/cvmx-csr.h>
#include <mach/cvmx-bootmem.h>
#include <mach/octeon-model.h>
#include <mach/cvmx-fuse.h>
#include <mach/octeon-feature.h>
#include <mach/cvmx-qlm.h>
#include <mach/octeon_qlm.h>
#include <mach/cvmx-pcie.h>
#include <mach/cvmx-coremask.h>

#include <mach/cvmx-fpa.h>
#include <mach/cvmx-cmd-queue.h>

#include <mach/cvmx-agl-defs.h>
#include <mach/cvmx-bgxx-defs.h>
#include <mach/cvmx-ciu-defs.h>
#include <mach/cvmx-gmxx-defs.h>
#include <mach/cvmx-gserx-defs.h>
#include <mach/cvmx-ilk-defs.h>
#include <mach/cvmx-ipd-defs.h>
#include <mach/cvmx-pcsx-defs.h>
#include <mach/cvmx-pcsxx-defs.h>
#include <mach/cvmx-pki-defs.h>
#include <mach/cvmx-pko-defs.h>
#include <mach/cvmx-xcv-defs.h>

#include <mach/cvmx-hwpko.h>
#include <mach/cvmx-ilk.h>
#include <mach/cvmx-pki.h>
#include <mach/cvmx-pko3.h>
#include <mach/cvmx-pko3-queue.h>
#include <mach/cvmx-pko3-resources.h>

#include <mach/cvmx-helper.h>
#include <mach/cvmx-helper-board.h>
#include <mach/cvmx-helper-cfg.h>

#include <mach/cvmx-helper-bgx.h>
#include <mach/cvmx-helper-cfg.h>
#include <mach/cvmx-helper-util.h>
#include <mach/cvmx-helper-pki.h>

#include <mach/cvmx-helper-util.h>
#include <mach/cvmx-dpi-defs.h>
#include <mach/cvmx-npei-defs.h>
#include <mach/cvmx-pexp-defs.h>

/**
 * This application uses this pointer to access the global queue
 * state. It points to a bootmem named block.
 */
__cvmx_cmd_queue_all_state_t *__cvmx_cmd_queue_state_ptrs[CVMX_MAX_NODES];

/**
 * @INTERNAL
 * Initialize the Global queue state pointer.
 *
 * @return CVMX_CMD_QUEUE_SUCCESS or a failure code
 */
cvmx_cmd_queue_result_t __cvmx_cmd_queue_init_state_ptr(unsigned int node)
{
	const char *alloc_name = "cvmx_cmd_queues\0\0";
	char s[4] = "_0";
	const struct cvmx_bootmem_named_block_desc *block_desc = NULL;
	unsigned int size;
	u64 paddr_min = 0, paddr_max = 0;
	void *ptr;

	if (cvmx_likely(__cvmx_cmd_queue_state_ptrs[node]))
		return CVMX_CMD_QUEUE_SUCCESS;

	/* Add node# to block name */
	if (node > 0) {
		s[1] += node;
		strcat((char *)alloc_name, s);
	}

	/* Find the named block in case it has been created already */
	block_desc = cvmx_bootmem_find_named_block(alloc_name);
	if (block_desc) {
		__cvmx_cmd_queue_state_ptrs[node] =
			(__cvmx_cmd_queue_all_state_t *)cvmx_phys_to_ptr(
				block_desc->base_addr);
		return CVMX_CMD_QUEUE_SUCCESS;
	}

	size = sizeof(*__cvmx_cmd_queue_state_ptrs[node]);

	/* Rest f the code is to allocate a new named block */

	/* Atomically allocate named block once, and zero it by default */
	ptr = cvmx_bootmem_alloc_named_range_once(size, paddr_min, paddr_max,
						  128, alloc_name, NULL);

	if (ptr) {
		__cvmx_cmd_queue_state_ptrs[node] =
			(__cvmx_cmd_queue_all_state_t *)ptr;
	} else {
		debug("ERROR: %s: Unable to get named block %s.\n", __func__,
		      alloc_name);
		return CVMX_CMD_QUEUE_NO_MEMORY;
	}
	return CVMX_CMD_QUEUE_SUCCESS;
}

/**
 * Initialize a command queue for use. The initial FPA buffer is
 * allocated and the hardware unit is configured to point to the
 * new command queue.
 *
 * @param queue_id  Hardware command queue to initialize.
 * @param max_depth Maximum outstanding commands that can be queued.
 * @param fpa_pool  FPA pool the command queues should come from.
 * @param pool_size Size of each buffer in the FPA pool (bytes)
 *
 * @return CVMX_CMD_QUEUE_SUCCESS or a failure code
 */
cvmx_cmd_queue_result_t cvmx_cmd_queue_initialize(cvmx_cmd_queue_id_t queue_id,
						  int max_depth, int fpa_pool,
						  int pool_size)
{
	__cvmx_cmd_queue_state_t *qstate;
	cvmx_cmd_queue_result_t result;
	unsigned int node;
	unsigned int index;
	int fpa_pool_min, fpa_pool_max;
	union cvmx_fpa_ctl_status status;
	void *buffer;

	node = __cvmx_cmd_queue_get_node(queue_id);

	index = __cvmx_cmd_queue_get_index(queue_id);
	if (index >= NUM_ELEMENTS(__cvmx_cmd_queue_state_ptrs[node]->state)) {
		printf("ERROR: %s: queue %#x out of range\n", __func__,
		       queue_id);
		return CVMX_CMD_QUEUE_INVALID_PARAM;
	}

	result = __cvmx_cmd_queue_init_state_ptr(node);
	if (result != CVMX_CMD_QUEUE_SUCCESS)
		return result;

	qstate = __cvmx_cmd_queue_get_state(queue_id);
	if (!qstate)
		return CVMX_CMD_QUEUE_INVALID_PARAM;

	/*
	 * We artificially limit max_depth to 1<<20 words. It is an
	 * arbitrary limit.
	 */
	if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH) {
		if (max_depth < 0 || max_depth > 1 << 20)
			return CVMX_CMD_QUEUE_INVALID_PARAM;
	} else if (max_depth != 0) {
		return CVMX_CMD_QUEUE_INVALID_PARAM;
	}

	/* CVMX_FPA_NUM_POOLS maps to cvmx_fpa3_num_auras for FPA3 */
	fpa_pool_min = node << 10;
	fpa_pool_max = fpa_pool_min + CVMX_FPA_NUM_POOLS;

	if (fpa_pool < fpa_pool_min || fpa_pool >= fpa_pool_max)
		return CVMX_CMD_QUEUE_INVALID_PARAM;

	if (pool_size < 128 || pool_size > (1 << 17))
		return CVMX_CMD_QUEUE_INVALID_PARAM;

	if (pool_size & 3)
		debug("WARNING: %s: pool_size %d not multiple of 8\n", __func__,
		      pool_size);

	/* See if someone else has already initialized the queue */
	if (qstate->base_paddr) {
		int depth;
		static const char emsg[] = /* Common error message part */
			"Queue already initialized with different ";

		depth = (max_depth + qstate->pool_size_m1 - 1) /
			qstate->pool_size_m1;
		if (depth != qstate->max_depth) {
			depth = qstate->max_depth * qstate->pool_size_m1;
			debug("ERROR: %s: %s max_depth (%d).\n", __func__, emsg,
			      depth);
			return CVMX_CMD_QUEUE_INVALID_PARAM;
		}
		if (fpa_pool != qstate->fpa_pool) {
			debug("ERROR: %s: %s FPA pool (%d).\n", __func__, emsg,
			      (int)qstate->fpa_pool);
			return CVMX_CMD_QUEUE_INVALID_PARAM;
		}
		if ((pool_size >> 3) - 1 != qstate->pool_size_m1) {
			debug("ERROR: %s: %s FPA pool size (%u).\n", __func__,
			      emsg, (qstate->pool_size_m1 + 1) << 3);
			return CVMX_CMD_QUEUE_INVALID_PARAM;
		}
		return CVMX_CMD_QUEUE_ALREADY_SETUP;
	}

	if (!(octeon_has_feature(OCTEON_FEATURE_FPA3))) {
		status.u64 = csr_rd(CVMX_FPA_CTL_STATUS);
		if (!status.s.enb) {
			debug("ERROR: %s: FPA is not enabled.\n",
			      __func__);
			return CVMX_CMD_QUEUE_NO_MEMORY;
		}
	}
	buffer = cvmx_fpa_alloc(fpa_pool);
	if (!buffer) {
		debug("ERROR: %s: allocating first buffer.\n", __func__);
		return CVMX_CMD_QUEUE_NO_MEMORY;
	}

	index = (pool_size >> 3) - 1;
	qstate->pool_size_m1 = index;
	qstate->max_depth = (max_depth + index - 1) / index;
	qstate->index = 0;
	qstate->fpa_pool = fpa_pool;
	qstate->base_paddr = cvmx_ptr_to_phys(buffer);

	/* Initialize lock */
	__cvmx_cmd_queue_lock_init(queue_id);
	return CVMX_CMD_QUEUE_SUCCESS;
}

/**
 * Return the command buffer to be written to. The purpose of this
 * function is to allow CVMX routine access to the low level buffer
 * for initial hardware setup. User applications should not call this
 * function directly.
 *
 * @param queue_id Command queue to query
 *
 * @return Command buffer or NULL on failure
 */
void *cvmx_cmd_queue_buffer(cvmx_cmd_queue_id_t queue_id)
{
	__cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id);

	if (qptr && qptr->base_paddr)
		return cvmx_phys_to_ptr((u64)qptr->base_paddr);
	else
		return NULL;
}

static u64 *__cvmx_cmd_queue_add_blk(__cvmx_cmd_queue_state_t *qptr)
{
	u64 *cmd_ptr;
	u64 *new_buffer;
	u64 new_paddr;

	/* Get base vaddr of current (full) block */
	cmd_ptr = (u64 *)cvmx_phys_to_ptr((u64)qptr->base_paddr);

	/* Allocate a new block from the per-queue pool */
	new_buffer = (u64 *)cvmx_fpa_alloc(qptr->fpa_pool);

	/* Check for allocation failure */
	if (cvmx_unlikely(!new_buffer))
		return NULL;

	/* Zero out the new block link pointer,
	 * in case this block will be filled to the rim
	 */
	new_buffer[qptr->pool_size_m1] = ~0ull;

	/* Get physical address of the new buffer */
	new_paddr = cvmx_ptr_to_phys(new_buffer);

	/* Store the physical link address at the end of current full block */
	cmd_ptr[qptr->pool_size_m1] = new_paddr;

	/* Store the physical address in the queue state structure */
	qptr->base_paddr = new_paddr;
	qptr->index = 0;

	/* Return the virtual base of the new block */
	return new_buffer;
}

/**
 * @INTERNAL
 * Add command words into a queue, handles all the corener cases
 * where only some of the words might fit into the current block,
 * and a new block may need to be allocated.
 * Locking and argument checks are done in the front-end in-line
 * functions that call this one for the rare corner cases.
 */
cvmx_cmd_queue_result_t
__cvmx_cmd_queue_write_raw(cvmx_cmd_queue_id_t queue_id,
			   __cvmx_cmd_queue_state_t *qptr, int cmd_count,
			   const u64 *cmds)
{
	u64 *cmd_ptr;
	unsigned int index;

	cmd_ptr = (u64 *)cvmx_phys_to_ptr((u64)qptr->base_paddr);
	index = qptr->index;

	/* Enforce queue depth limit, if enabled, once per block */
	if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH && cvmx_unlikely(qptr->max_depth)) {
		unsigned int depth = cvmx_cmd_queue_length(queue_id);

		depth /= qptr->pool_size_m1;

		if (cvmx_unlikely(depth > qptr->max_depth))
			return CVMX_CMD_QUEUE_FULL;
	}

	/*
	 * If the block allocation fails, even the words that we wrote
	 * to the current block will not count because the 'index' will
	 * not be comitted.
	 * The loop is run 'count + 1' times to take care of the tail
	 * case, where the buffer is full to the rim, so the link
	 * pointer must be filled with a valid address.
	 */
	while (cmd_count >= 0) {
		if (index >= qptr->pool_size_m1) {
			/* Block is full, get another one and proceed */
			cmd_ptr = __cvmx_cmd_queue_add_blk(qptr);

			/* Baul on allocation error w/o comitting anything */
			if (cvmx_unlikely(!cmd_ptr))
				return CVMX_CMD_QUEUE_NO_MEMORY;

			/* Reset index for start of new block */
			index = 0;
		}
		/* Exit Loop on 'count + 1' iterations */
		if (cmd_count <= 0)
			break;
		/* Store commands into queue block while there is space */
		cmd_ptr[index++] = *cmds++;
		cmd_count--;
	} /* while cmd_count */

	/* Commit added words if all is well */
	qptr->index = index;

	return CVMX_CMD_QUEUE_SUCCESS;
}