diff options
Diffstat (limited to 'arch/mips/mach-octeon/include/mach')
29 files changed, 12242 insertions, 0 deletions
| diff --git a/arch/mips/mach-octeon/include/mach/cvmx-address.h b/arch/mips/mach-octeon/include/mach/cvmx-address.h new file mode 100644 index 00000000000..984f574a75b --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-address.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Typedefs and defines for working with Octeon physical addresses. + */ + +#ifndef __CVMX_ADDRESS_H__ +#define __CVMX_ADDRESS_H__ + +typedef enum { +	CVMX_MIPS_SPACE_XKSEG = 3LL, +	CVMX_MIPS_SPACE_XKPHYS = 2LL, +	CVMX_MIPS_SPACE_XSSEG = 1LL, +	CVMX_MIPS_SPACE_XUSEG = 0LL +} cvmx_mips_space_t; + +typedef enum { +	CVMX_MIPS_XKSEG_SPACE_KSEG0 = 0LL, +	CVMX_MIPS_XKSEG_SPACE_KSEG1 = 1LL, +	CVMX_MIPS_XKSEG_SPACE_SSEG = 2LL, +	CVMX_MIPS_XKSEG_SPACE_KSEG3 = 3LL +} cvmx_mips_xkseg_space_t; + +/* decodes <14:13> of a kseg3 window address */ +typedef enum { +	CVMX_ADD_WIN_SCR = 0L, +	CVMX_ADD_WIN_DMA = 1L, +	CVMX_ADD_WIN_UNUSED = 2L, +	CVMX_ADD_WIN_UNUSED2 = 3L +} cvmx_add_win_dec_t; + +/* decode within DMA space */ +typedef enum { +	CVMX_ADD_WIN_DMA_ADD = 0L, +	CVMX_ADD_WIN_DMA_SENDMEM = 1L, +	/* store data must be normal DRAM memory space address in this case */ +	CVMX_ADD_WIN_DMA_SENDDMA = 2L, +	/* see CVMX_ADD_WIN_DMA_SEND_DEC for data contents */ +	CVMX_ADD_WIN_DMA_SENDIO = 3L, +	/* store data must be normal IO space address in this case */ +	CVMX_ADD_WIN_DMA_SENDSINGLE = 4L, +	/* no write buffer data needed/used */ +} cvmx_add_win_dma_dec_t; + +/** + *   Physical Address Decode + * + * Octeon-I HW never interprets this X (<39:36> reserved + * for future expansion), software should set to 0. + * + *  - 0x0 XXX0 0000 0000 to      DRAM         Cached + *  - 0x0 XXX0 0FFF FFFF + * + *  - 0x0 XXX0 1000 0000 to      Boot Bus     Uncached  (Converted to 0x1 00X0 1000 0000 + *  - 0x0 XXX0 1FFF FFFF         + EJTAG                           to 0x1 00X0 1FFF FFFF) + * + *  - 0x0 XXX0 2000 0000 to      DRAM         Cached + *  - 0x0 XXXF FFFF FFFF + * + *  - 0x1 00X0 0000 0000 to      Boot Bus     Uncached + *  - 0x1 00XF FFFF FFFF + * + *  - 0x1 01X0 0000 0000 to      Other NCB    Uncached + *  - 0x1 FFXF FFFF FFFF         devices + * + * Decode of all Octeon addresses + */ +typedef union { +	u64 u64; +	struct { +		cvmx_mips_space_t R : 2; +		u64 offset : 62; +	} sva; + +	struct { +		u64 zeroes : 33; +		u64 offset : 31; +	} suseg; + +	struct { +		u64 ones : 33; +		cvmx_mips_xkseg_space_t sp : 2; +		u64 offset : 29; +	} sxkseg; + +	struct { +		cvmx_mips_space_t R : 2; +		u64 cca : 3; +		u64 mbz : 10; +		u64 pa : 49; +	} sxkphys; + +	struct { +		u64 mbz : 15; +		u64 is_io : 1; +		u64 did : 8; +		u64 unaddr : 4; +		u64 offset : 36; +	} sphys; + +	struct { +		u64 zeroes : 24; +		u64 unaddr : 4; +		u64 offset : 36; +	} smem; + +	struct { +		u64 mem_region : 2; +		u64 mbz : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 unaddr : 4; +		u64 offset : 36; +	} sio; + +	struct { +		u64 ones : 49; +		cvmx_add_win_dec_t csrdec : 2; +		u64 addr : 13; +	} sscr; + +	/* there should only be stores to IOBDMA space, no loads */ +	struct { +		u64 ones : 49; +		cvmx_add_win_dec_t csrdec : 2; +		u64 unused2 : 3; +		cvmx_add_win_dma_dec_t type : 3; +		u64 addr : 7; +	} sdma; + +	struct { +		u64 didspace : 24; +		u64 unused : 40; +	} sfilldidspace; +} cvmx_addr_t; + +/* These macros for used by 32 bit applications */ + +#define CVMX_MIPS32_SPACE_KSEG0	     1l +#define CVMX_ADD_SEG32(segment, add) (((s32)segment << 31) | (s32)(add)) + +/* + * Currently all IOs are performed using XKPHYS addressing. Linux uses the + * CvmMemCtl register to enable XKPHYS addressing to IO space from user mode. + * Future OSes may need to change the upper bits of IO addresses. The + * following define controls the upper two bits for all IO addresses generated + * by the simple executive library + */ +#define CVMX_IO_SEG CVMX_MIPS_SPACE_XKPHYS + +/* These macros simplify the process of creating common IO addresses */ +#define CVMX_ADD_SEG(segment, add) ((((u64)segment) << 62) | (add)) + +#define CVMX_ADD_IO_SEG(add) (add) + +#define CVMX_ADDR_DIDSPACE(did)	   (((CVMX_IO_SEG) << 22) | ((1ULL) << 8) | (did)) +#define CVMX_ADDR_DID(did)	   (CVMX_ADDR_DIDSPACE(did) << 40) +#define CVMX_FULL_DID(did, subdid) (((did) << 3) | (subdid)) + +/* from include/ncb_rsl_id.v */ +#define CVMX_OCT_DID_MIS  0ULL /* misc stuff */ +#define CVMX_OCT_DID_GMX0 1ULL +#define CVMX_OCT_DID_GMX1 2ULL +#define CVMX_OCT_DID_PCI  3ULL +#define CVMX_OCT_DID_KEY  4ULL +#define CVMX_OCT_DID_FPA  5ULL +#define CVMX_OCT_DID_DFA  6ULL +#define CVMX_OCT_DID_ZIP  7ULL +#define CVMX_OCT_DID_RNG  8ULL +#define CVMX_OCT_DID_IPD  9ULL +#define CVMX_OCT_DID_PKT  10ULL +#define CVMX_OCT_DID_TIM  11ULL +#define CVMX_OCT_DID_TAG  12ULL +/* the rest are not on the IO bus */ +#define CVMX_OCT_DID_L2C  16ULL +#define CVMX_OCT_DID_LMC  17ULL +#define CVMX_OCT_DID_SPX0 18ULL +#define CVMX_OCT_DID_SPX1 19ULL +#define CVMX_OCT_DID_PIP  20ULL +#define CVMX_OCT_DID_ASX0 22ULL +#define CVMX_OCT_DID_ASX1 23ULL +#define CVMX_OCT_DID_IOB  30ULL + +#define CVMX_OCT_DID_PKT_SEND	 CVMX_FULL_DID(CVMX_OCT_DID_PKT, 2ULL) +#define CVMX_OCT_DID_TAG_SWTAG	 CVMX_FULL_DID(CVMX_OCT_DID_TAG, 0ULL) +#define CVMX_OCT_DID_TAG_TAG1	 CVMX_FULL_DID(CVMX_OCT_DID_TAG, 1ULL) +#define CVMX_OCT_DID_TAG_TAG2	 CVMX_FULL_DID(CVMX_OCT_DID_TAG, 2ULL) +#define CVMX_OCT_DID_TAG_TAG3	 CVMX_FULL_DID(CVMX_OCT_DID_TAG, 3ULL) +#define CVMX_OCT_DID_TAG_NULL_RD CVMX_FULL_DID(CVMX_OCT_DID_TAG, 4ULL) +#define CVMX_OCT_DID_TAG_TAG5	 CVMX_FULL_DID(CVMX_OCT_DID_TAG, 5ULL) +#define CVMX_OCT_DID_TAG_CSR	 CVMX_FULL_DID(CVMX_OCT_DID_TAG, 7ULL) +#define CVMX_OCT_DID_FAU_FAI	 CVMX_FULL_DID(CVMX_OCT_DID_IOB, 0ULL) +#define CVMX_OCT_DID_TIM_CSR	 CVMX_FULL_DID(CVMX_OCT_DID_TIM, 0ULL) +#define CVMX_OCT_DID_KEY_RW	 CVMX_FULL_DID(CVMX_OCT_DID_KEY, 0ULL) +#define CVMX_OCT_DID_PCI_6	 CVMX_FULL_DID(CVMX_OCT_DID_PCI, 6ULL) +#define CVMX_OCT_DID_MIS_BOO	 CVMX_FULL_DID(CVMX_OCT_DID_MIS, 0ULL) +#define CVMX_OCT_DID_PCI_RML	 CVMX_FULL_DID(CVMX_OCT_DID_PCI, 0ULL) +#define CVMX_OCT_DID_IPD_CSR	 CVMX_FULL_DID(CVMX_OCT_DID_IPD, 7ULL) +#define CVMX_OCT_DID_DFA_CSR	 CVMX_FULL_DID(CVMX_OCT_DID_DFA, 7ULL) +#define CVMX_OCT_DID_MIS_CSR	 CVMX_FULL_DID(CVMX_OCT_DID_MIS, 7ULL) +#define CVMX_OCT_DID_ZIP_CSR	 CVMX_FULL_DID(CVMX_OCT_DID_ZIP, 0ULL) + +/* Cast to unsigned long long, mainly for use in printfs. */ +#define CAST_ULL(v) ((unsigned long long)(v)) + +#define UNMAPPED_PTR(x) ((1ULL << 63) | (x)) + +#endif /* __CVMX_ADDRESS_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-cmd-queue.h b/arch/mips/mach-octeon/include/mach/cvmx-cmd-queue.h new file mode 100644 index 00000000000..ddc294348cb --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-cmd-queue.h @@ -0,0 +1,441 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Support functions for managing command queues used for + * various hardware blocks. + * + * The common command queue infrastructure abstracts out the + * software necessary for adding to Octeon's chained queue + * structures. These structures are used for commands to the + * PKO, ZIP, DFA, RAID, HNA, and DMA engine blocks. Although each + * hardware unit takes commands and CSRs of different types, + * they all use basic linked command buffers to store the + * pending request. In general, users of the CVMX API don't + * call cvmx-cmd-queue functions directly. Instead the hardware + * unit specific wrapper should be used. The wrappers perform + * unit specific validation and CSR writes to submit the + * commands. + * + * Even though most software will never directly interact with + * cvmx-cmd-queue, knowledge of its internal workings can help + * in diagnosing performance problems and help with debugging. + * + * Command queue pointers are stored in a global named block + * called "cvmx_cmd_queues". Except for the PKO queues, each + * hardware queue is stored in its own cache line to reduce SMP + * contention on spin locks. The PKO queues are stored such that + * every 16th queue is next to each other in memory. This scheme + * allows for queues being in separate cache lines when there + * are low number of queues per port. With 16 queues per port, + * the first queue for each port is in the same cache area. The + * second queues for each port are in another area, etc. This + * allows software to implement very efficient lockless PKO with + * 16 queues per port using a minimum of cache lines per core. + * All queues for a given core will be isolated in the same + * cache area. + * + * In addition to the memory pointer layout, cvmx-cmd-queue + * provides an optimized fair ll/sc locking mechanism for the + * queues. The lock uses a "ticket / now serving" model to + * maintain fair order on contended locks. In addition, it uses + * predicted locking time to limit cache contention. When a core + * know it must wait in line for a lock, it spins on the + * internal cycle counter to completely eliminate any causes of + * bus traffic. + */ + +#ifndef __CVMX_CMD_QUEUE_H__ +#define __CVMX_CMD_QUEUE_H__ + +/** + * By default we disable the max depth support. Most programs + * don't use it and it slows down the command queue processing + * significantly. + */ +#ifndef CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH +#define CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH 0 +#endif + +/** + * Enumeration representing all hardware blocks that use command + * queues. Each hardware block has up to 65536 sub identifiers for + * multiple command queues. Not all chips support all hardware + * units. + */ +typedef enum { +	CVMX_CMD_QUEUE_PKO_BASE = 0x00000, +#define CVMX_CMD_QUEUE_PKO(queue)                                                                  \ +	((cvmx_cmd_queue_id_t)(CVMX_CMD_QUEUE_PKO_BASE + (0xffff & (queue)))) +	CVMX_CMD_QUEUE_ZIP = 0x10000, +#define CVMX_CMD_QUEUE_ZIP_QUE(queue)                                                              \ +	((cvmx_cmd_queue_id_t)(CVMX_CMD_QUEUE_ZIP + (0xffff & (queue)))) +	CVMX_CMD_QUEUE_DFA = 0x20000, +	CVMX_CMD_QUEUE_RAID = 0x30000, +	CVMX_CMD_QUEUE_DMA_BASE = 0x40000, +#define CVMX_CMD_QUEUE_DMA(queue)                                                                  \ +	((cvmx_cmd_queue_id_t)(CVMX_CMD_QUEUE_DMA_BASE + (0xffff & (queue)))) +	CVMX_CMD_QUEUE_BCH = 0x50000, +#define CVMX_CMD_QUEUE_BCH(queue) ((cvmx_cmd_queue_id_t)(CVMX_CMD_QUEUE_BCH + (0xffff & (queue)))) +	CVMX_CMD_QUEUE_HNA = 0x60000, +	CVMX_CMD_QUEUE_END = 0x70000, +} cvmx_cmd_queue_id_t; + +#define CVMX_CMD_QUEUE_ZIP3_QUE(node, queue)                                                       \ +	((cvmx_cmd_queue_id_t)((node) << 24 | CVMX_CMD_QUEUE_ZIP | (0xffff & (queue)))) + +/** + * Command write operations can fail if the command queue needs + * a new buffer and the associated FPA pool is empty. It can also + * fail if the number of queued command words reaches the maximum + * set at initialization. + */ +typedef enum { +	CVMX_CMD_QUEUE_SUCCESS = 0, +	CVMX_CMD_QUEUE_NO_MEMORY = -1, +	CVMX_CMD_QUEUE_FULL = -2, +	CVMX_CMD_QUEUE_INVALID_PARAM = -3, +	CVMX_CMD_QUEUE_ALREADY_SETUP = -4, +} cvmx_cmd_queue_result_t; + +typedef struct { +	/* First 64-bit word: */ +	u64 fpa_pool : 16; +	u64 base_paddr : 48; +	s32 index; +	u16 max_depth; +	u16 pool_size_m1; +} __cvmx_cmd_queue_state_t; + +/** + * command-queue locking uses a fair ticket spinlock algo, + * with 64-bit tickets for endianness-neutrality and + * counter overflow protection. + * Lock is free when both counters are of equal value. + */ +typedef struct { +	u64 ticket; +	u64 now_serving; +} __cvmx_cmd_queue_lock_t; + +/** + * @INTERNAL + * This structure contains the global state of all command queues. + * It is stored in a bootmem named block and shared by all + * applications running on Octeon. Tickets are stored in a different + * cache line that queue information to reduce the contention on the + * ll/sc used to get a ticket. If this is not the case, the update + * of queue state causes the ll/sc to fail quite often. + */ +typedef struct { +	__cvmx_cmd_queue_lock_t lock[(CVMX_CMD_QUEUE_END >> 16) * 256]; +	__cvmx_cmd_queue_state_t state[(CVMX_CMD_QUEUE_END >> 16) * 256]; +} __cvmx_cmd_queue_all_state_t; + +extern __cvmx_cmd_queue_all_state_t *__cvmx_cmd_queue_state_ptrs[CVMX_MAX_NODES]; + +/** + * @INTERNAL + * Internal function to handle the corner cases + * of adding command words to a queue when the current + * block is getting full. + */ +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); + +/** + * 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); + +/** + * Shutdown a queue a free it's command buffers to the FPA. The + * hardware connected to the queue must be stopped before this + * function is called. + * + * @param queue_id Queue to shutdown + * + * @return CVMX_CMD_QUEUE_SUCCESS or a failure code + */ +cvmx_cmd_queue_result_t cvmx_cmd_queue_shutdown(cvmx_cmd_queue_id_t queue_id); + +/** + * Return the number of command words pending in the queue. This + * function may be relatively slow for some hardware units. + * + * @param queue_id Hardware command queue to query + * + * @return Number of outstanding commands + */ +int cvmx_cmd_queue_length(cvmx_cmd_queue_id_t queue_id); + +/** + * 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); + +/** + * @INTERNAL + * Retrieve or allocate command queue state named block + */ +cvmx_cmd_queue_result_t __cvmx_cmd_queue_init_state_ptr(unsigned int node); + +/** + * @INTERNAL + * Get the index into the state arrays for the supplied queue id. + * + * @param queue_id Queue ID to get an index for + * + * @return Index into the state arrays + */ +static inline unsigned int __cvmx_cmd_queue_get_index(cvmx_cmd_queue_id_t queue_id) +{ +	/* Warning: This code currently only works with devices that have 256 +	 * queues or less.  Devices with more than 16 queues are laid out in +	 * memory to allow cores quick access to every 16th queue. This reduces +	 * cache thrashing when you are running 16 queues per port to support +	 * lockless operation +	 */ +	unsigned int unit = (queue_id >> 16) & 0xff; +	unsigned int q = (queue_id >> 4) & 0xf; +	unsigned int core = queue_id & 0xf; + +	return (unit << 8) | (core << 4) | q; +} + +static inline int __cvmx_cmd_queue_get_node(cvmx_cmd_queue_id_t queue_id) +{ +	unsigned int node = queue_id >> 24; +	return node; +} + +/** + * @INTERNAL + * Lock the supplied queue so nobody else is updating it at the same + * time as us. + * + * @param queue_id Queue ID to lock + * + */ +static inline void __cvmx_cmd_queue_lock(cvmx_cmd_queue_id_t queue_id) +{ +} + +/** + * @INTERNAL + * Unlock the queue, flushing all writes. + * + * @param queue_id Queue ID to lock + * + */ +static inline void __cvmx_cmd_queue_unlock(cvmx_cmd_queue_id_t queue_id) +{ +	CVMX_SYNCWS; /* nudge out the unlock. */ +} + +/** + * @INTERNAL + * Initialize a command-queue lock to "unlocked" state. + */ +static inline void __cvmx_cmd_queue_lock_init(cvmx_cmd_queue_id_t queue_id) +{ +	unsigned int index = __cvmx_cmd_queue_get_index(queue_id); +	unsigned int node = __cvmx_cmd_queue_get_node(queue_id); + +	__cvmx_cmd_queue_state_ptrs[node]->lock[index] = (__cvmx_cmd_queue_lock_t){ 0, 0 }; +	CVMX_SYNCWS; +} + +/** + * @INTERNAL + * Get the queue state structure for the given queue id + * + * @param queue_id Queue id to get + * + * @return Queue structure or NULL on failure + */ +static inline __cvmx_cmd_queue_state_t *__cvmx_cmd_queue_get_state(cvmx_cmd_queue_id_t queue_id) +{ +	unsigned int index; +	unsigned int node; +	__cvmx_cmd_queue_state_t *qptr; + +	node = __cvmx_cmd_queue_get_node(queue_id); +	index = __cvmx_cmd_queue_get_index(queue_id); + +	if (cvmx_unlikely(!__cvmx_cmd_queue_state_ptrs[node])) +		__cvmx_cmd_queue_init_state_ptr(node); + +	qptr = &__cvmx_cmd_queue_state_ptrs[node]->state[index]; +	return qptr; +} + +/** + * Write an arbitrary number of command words to a command queue. + * This is a generic function; the fixed number of command word + * functions yield higher performance. + * + * @param queue_id  Hardware command queue to write to + * @param use_locking + *                  Use internal locking to ensure exclusive access for queue + *                  updates. If you don't use this locking you must ensure + *                  exclusivity some other way. Locking is strongly recommended. + * @param cmd_count Number of command words to write + * @param cmds      Array of commands to write + * + * @return CVMX_CMD_QUEUE_SUCCESS or a failure code + */ +static inline cvmx_cmd_queue_result_t +cvmx_cmd_queue_write(cvmx_cmd_queue_id_t queue_id, bool use_locking, int cmd_count, const u64 *cmds) +{ +	cvmx_cmd_queue_result_t ret = CVMX_CMD_QUEUE_SUCCESS; +	u64 *cmd_ptr; + +	__cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); + +	/* Make sure nobody else is updating the same queue */ +	if (cvmx_likely(use_locking)) +		__cvmx_cmd_queue_lock(queue_id); + +	/* Most of the time there is lots of free words in current block */ +	if (cvmx_unlikely((qptr->index + cmd_count) >= qptr->pool_size_m1)) { +		/* The rare case when nearing end of block */ +		ret = __cvmx_cmd_queue_write_raw(queue_id, qptr, cmd_count, cmds); +	} else { +		cmd_ptr = (u64 *)cvmx_phys_to_ptr((u64)qptr->base_paddr); +		/* Loop easy for compiler to unroll for the likely case */ +		while (cmd_count > 0) { +			cmd_ptr[qptr->index++] = *cmds++; +			cmd_count--; +		} +	} + +	/* All updates are complete. Release the lock and return */ +	if (cvmx_likely(use_locking)) +		__cvmx_cmd_queue_unlock(queue_id); +	else +		CVMX_SYNCWS; + +	return ret; +} + +/** + * Simple function to write two command words to a command queue. + * + * @param queue_id Hardware command queue to write to + * @param use_locking + *                 Use internal locking to ensure exclusive access for queue + *                 updates. If you don't use this locking you must ensure + *                 exclusivity some other way. Locking is strongly recommended. + * @param cmd1     Command + * @param cmd2     Command + * + * @return CVMX_CMD_QUEUE_SUCCESS or a failure code + */ +static inline cvmx_cmd_queue_result_t cvmx_cmd_queue_write2(cvmx_cmd_queue_id_t queue_id, +							    bool use_locking, u64 cmd1, u64 cmd2) +{ +	cvmx_cmd_queue_result_t ret = CVMX_CMD_QUEUE_SUCCESS; +	u64 *cmd_ptr; + +	__cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); + +	/* Make sure nobody else is updating the same queue */ +	if (cvmx_likely(use_locking)) +		__cvmx_cmd_queue_lock(queue_id); + +	if (cvmx_unlikely((qptr->index + 2) >= qptr->pool_size_m1)) { +		/* The rare case when nearing end of block */ +		u64 cmds[2]; + +		cmds[0] = cmd1; +		cmds[1] = cmd2; +		ret = __cvmx_cmd_queue_write_raw(queue_id, qptr, 2, cmds); +	} else { +		/* Likely case to work fast */ +		cmd_ptr = (u64 *)cvmx_phys_to_ptr((u64)qptr->base_paddr); +		cmd_ptr += qptr->index; +		qptr->index += 2; +		cmd_ptr[0] = cmd1; +		cmd_ptr[1] = cmd2; +	} + +	/* All updates are complete. Release the lock and return */ +	if (cvmx_likely(use_locking)) +		__cvmx_cmd_queue_unlock(queue_id); +	else +		CVMX_SYNCWS; + +	return ret; +} + +/** + * Simple function to write three command words to a command queue. + * + * @param queue_id Hardware command queue to write to + * @param use_locking + *                 Use internal locking to ensure exclusive access for queue + *                 updates. If you don't use this locking you must ensure + *                 exclusivity some other way. Locking is strongly recommended. + * @param cmd1     Command + * @param cmd2     Command + * @param cmd3     Command + * + * @return CVMX_CMD_QUEUE_SUCCESS or a failure code + */ +static inline cvmx_cmd_queue_result_t +cvmx_cmd_queue_write3(cvmx_cmd_queue_id_t queue_id, bool use_locking, u64 cmd1, u64 cmd2, u64 cmd3) +{ +	cvmx_cmd_queue_result_t ret = CVMX_CMD_QUEUE_SUCCESS; +	__cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); +	u64 *cmd_ptr; + +	/* Make sure nobody else is updating the same queue */ +	if (cvmx_likely(use_locking)) +		__cvmx_cmd_queue_lock(queue_id); + +	if (cvmx_unlikely((qptr->index + 3) >= qptr->pool_size_m1)) { +		/* Most of the time there is lots of free words in current block */ +		u64 cmds[3]; + +		cmds[0] = cmd1; +		cmds[1] = cmd2; +		cmds[2] = cmd3; +		ret = __cvmx_cmd_queue_write_raw(queue_id, qptr, 3, cmds); +	} else { +		cmd_ptr = (u64 *)cvmx_phys_to_ptr((u64)qptr->base_paddr); +		cmd_ptr += qptr->index; +		qptr->index += 3; +		cmd_ptr[0] = cmd1; +		cmd_ptr[1] = cmd2; +		cmd_ptr[2] = cmd3; +	} + +	/* All updates are complete. Release the lock and return */ +	if (cvmx_likely(use_locking)) +		__cvmx_cmd_queue_unlock(queue_id); +	else +		CVMX_SYNCWS; + +	return ret; +} + +#endif /* __CVMX_CMD_QUEUE_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-csr-enums.h b/arch/mips/mach-octeon/include/mach/cvmx-csr-enums.h new file mode 100644 index 00000000000..a8625b4228a --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-csr-enums.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Definitions for enumerations used with Octeon CSRs. + */ + +#ifndef __CVMX_CSR_ENUMS_H__ +#define __CVMX_CSR_ENUMS_H__ + +typedef enum { +	CVMX_IPD_OPC_MODE_STT = 0LL, +	CVMX_IPD_OPC_MODE_STF = 1LL, +	CVMX_IPD_OPC_MODE_STF1_STT = 2LL, +	CVMX_IPD_OPC_MODE_STF2_STT = 3LL +} cvmx_ipd_mode_t; + +/** + * Enumeration representing the amount of packet processing + * and validation performed by the input hardware. + */ +typedef enum { +	CVMX_PIP_PORT_CFG_MODE_NONE = 0ull, +	CVMX_PIP_PORT_CFG_MODE_SKIPL2 = 1ull, +	CVMX_PIP_PORT_CFG_MODE_SKIPIP = 2ull +} cvmx_pip_port_parse_mode_t; + +/** + * This enumeration controls how a QoS watcher matches a packet. + * + * @deprecated  This enumeration was used with cvmx_pip_config_watcher which has + *              been deprecated. + */ +typedef enum { +	CVMX_PIP_QOS_WATCH_DISABLE = 0ull, +	CVMX_PIP_QOS_WATCH_PROTNH = 1ull, +	CVMX_PIP_QOS_WATCH_TCP = 2ull, +	CVMX_PIP_QOS_WATCH_UDP = 3ull +} cvmx_pip_qos_watch_types; + +/** + * This enumeration is used in PIP tag config to control how + * POW tags are generated by the hardware. + */ +typedef enum { +	CVMX_PIP_TAG_MODE_TUPLE = 0ull, +	CVMX_PIP_TAG_MODE_MASK = 1ull, +	CVMX_PIP_TAG_MODE_IP_OR_MASK = 2ull, +	CVMX_PIP_TAG_MODE_TUPLE_XOR_MASK = 3ull +} cvmx_pip_tag_mode_t; + +/** + * Tag type definitions + */ +typedef enum { +	CVMX_POW_TAG_TYPE_ORDERED = 0L, +	CVMX_POW_TAG_TYPE_ATOMIC = 1L, +	CVMX_POW_TAG_TYPE_NULL = 2L, +	CVMX_POW_TAG_TYPE_NULL_NULL = 3L +} cvmx_pow_tag_type_t; + +/** + * LCR bits 0 and 1 control the number of bits per character. See the following table for encodings: + * + * - 00 = 5 bits (bits 0-4 sent) + * - 01 = 6 bits (bits 0-5 sent) + * - 10 = 7 bits (bits 0-6 sent) + * - 11 = 8 bits (all bits sent) + */ +typedef enum { +	CVMX_UART_BITS5 = 0, +	CVMX_UART_BITS6 = 1, +	CVMX_UART_BITS7 = 2, +	CVMX_UART_BITS8 = 3 +} cvmx_uart_bits_t; + +typedef enum { +	CVMX_UART_IID_NONE = 1, +	CVMX_UART_IID_RX_ERROR = 6, +	CVMX_UART_IID_RX_DATA = 4, +	CVMX_UART_IID_RX_TIMEOUT = 12, +	CVMX_UART_IID_TX_EMPTY = 2, +	CVMX_UART_IID_MODEM = 0, +	CVMX_UART_IID_BUSY = 7 +} cvmx_uart_iid_t; + +#endif /* __CVMX_CSR_ENUMS_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-csr.h b/arch/mips/mach-octeon/include/mach/cvmx-csr.h new file mode 100644 index 00000000000..730d54bb927 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-csr.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Configuration and status register (CSR) address and type definitions for + * Octoen. + */ + +#ifndef __CVMX_CSR_H__ +#define __CVMX_CSR_H__ + +#include "cvmx-csr-enums.h" +#include "cvmx-pip-defs.h" + +typedef cvmx_pip_prt_cfgx_t cvmx_pip_port_cfg_t; + +/* The CSRs for bootbus region zero used to be independent of the +    other 1-7. As of SDK 1.7.0 these were combined. These macros +    are for backwards compactability */ +#define CVMX_MIO_BOOT_REG_CFG0 CVMX_MIO_BOOT_REG_CFGX(0) +#define CVMX_MIO_BOOT_REG_TIM0 CVMX_MIO_BOOT_REG_TIMX(0) + +/* The CN3XXX and CN58XX chips used to not have a LMC number +    passed to the address macros. These are here to supply backwards +    compatibility with old code. Code should really use the new addresses +    with bus arguments for support on other chips */ +#define CVMX_LMC_BIST_CTL	  CVMX_LMCX_BIST_CTL(0) +#define CVMX_LMC_BIST_RESULT	  CVMX_LMCX_BIST_RESULT(0) +#define CVMX_LMC_COMP_CTL	  CVMX_LMCX_COMP_CTL(0) +#define CVMX_LMC_CTL		  CVMX_LMCX_CTL(0) +#define CVMX_LMC_CTL1		  CVMX_LMCX_CTL1(0) +#define CVMX_LMC_DCLK_CNT_HI	  CVMX_LMCX_DCLK_CNT_HI(0) +#define CVMX_LMC_DCLK_CNT_LO	  CVMX_LMCX_DCLK_CNT_LO(0) +#define CVMX_LMC_DCLK_CTL	  CVMX_LMCX_DCLK_CTL(0) +#define CVMX_LMC_DDR2_CTL	  CVMX_LMCX_DDR2_CTL(0) +#define CVMX_LMC_DELAY_CFG	  CVMX_LMCX_DELAY_CFG(0) +#define CVMX_LMC_DLL_CTL	  CVMX_LMCX_DLL_CTL(0) +#define CVMX_LMC_DUAL_MEMCFG	  CVMX_LMCX_DUAL_MEMCFG(0) +#define CVMX_LMC_ECC_SYND	  CVMX_LMCX_ECC_SYND(0) +#define CVMX_LMC_FADR		  CVMX_LMCX_FADR(0) +#define CVMX_LMC_IFB_CNT_HI	  CVMX_LMCX_IFB_CNT_HI(0) +#define CVMX_LMC_IFB_CNT_LO	  CVMX_LMCX_IFB_CNT_LO(0) +#define CVMX_LMC_MEM_CFG0	  CVMX_LMCX_MEM_CFG0(0) +#define CVMX_LMC_MEM_CFG1	  CVMX_LMCX_MEM_CFG1(0) +#define CVMX_LMC_OPS_CNT_HI	  CVMX_LMCX_OPS_CNT_HI(0) +#define CVMX_LMC_OPS_CNT_LO	  CVMX_LMCX_OPS_CNT_LO(0) +#define CVMX_LMC_PLL_BWCTL	  CVMX_LMCX_PLL_BWCTL(0) +#define CVMX_LMC_PLL_CTL	  CVMX_LMCX_PLL_CTL(0) +#define CVMX_LMC_PLL_STATUS	  CVMX_LMCX_PLL_STATUS(0) +#define CVMX_LMC_READ_LEVEL_CTL	  CVMX_LMCX_READ_LEVEL_CTL(0) +#define CVMX_LMC_READ_LEVEL_DBG	  CVMX_LMCX_READ_LEVEL_DBG(0) +#define CVMX_LMC_READ_LEVEL_RANKX CVMX_LMCX_READ_LEVEL_RANKX(0) +#define CVMX_LMC_RODT_COMP_CTL	  CVMX_LMCX_RODT_COMP_CTL(0) +#define CVMX_LMC_RODT_CTL	  CVMX_LMCX_RODT_CTL(0) +#define CVMX_LMC_WODT_CTL	  CVMX_LMCX_WODT_CTL0(0) +#define CVMX_LMC_WODT_CTL0	  CVMX_LMCX_WODT_CTL0(0) +#define CVMX_LMC_WODT_CTL1	  CVMX_LMCX_WODT_CTL1(0) + +/* The CN3XXX and CN58XX chips used to not have a TWSI bus number +    passed to the address macros. These are here to supply backwards +    compatibility with old code. Code should really use the new addresses +    with bus arguments for support on other chips */ +#define CVMX_MIO_TWS_INT	 CVMX_MIO_TWSX_INT(0) +#define CVMX_MIO_TWS_SW_TWSI	 CVMX_MIO_TWSX_SW_TWSI(0) +#define CVMX_MIO_TWS_SW_TWSI_EXT CVMX_MIO_TWSX_SW_TWSI_EXT(0) +#define CVMX_MIO_TWS_TWSI_SW	 CVMX_MIO_TWSX_TWSI_SW(0) + +/* The CN3XXX and CN58XX chips used to not have a SMI/MDIO bus number +    passed to the address macros. These are here to supply backwards +    compatibility with old code. Code should really use the new addresses +    with bus arguments for support on other chips */ +#define CVMX_SMI_CLK	CVMX_SMIX_CLK(0) +#define CVMX_SMI_CMD	CVMX_SMIX_CMD(0) +#define CVMX_SMI_EN	CVMX_SMIX_EN(0) +#define CVMX_SMI_RD_DAT CVMX_SMIX_RD_DAT(0) +#define CVMX_SMI_WR_DAT CVMX_SMIX_WR_DAT(0) + +#endif /* __CVMX_CSR_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-error.h b/arch/mips/mach-octeon/include/mach/cvmx-error.h new file mode 100644 index 00000000000..9a13ed42248 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-error.h @@ -0,0 +1,456 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Interface to the Octeon extended error status. + */ + +#ifndef __CVMX_ERROR_H__ +#define __CVMX_ERROR_H__ + +/** + * There are generally many error status bits associated with a + * single logical group. The enumeration below is used to + * communicate high level groups to the error infastructure so + * error status bits can be enable or disabled in large groups. + */ +typedef enum { +	CVMX_ERROR_GROUP_INTERNAL, +	CVMX_ERROR_GROUP_L2C, +	CVMX_ERROR_GROUP_ETHERNET, +	CVMX_ERROR_GROUP_MGMT_PORT, +	CVMX_ERROR_GROUP_PCI, +	CVMX_ERROR_GROUP_SRIO, +	CVMX_ERROR_GROUP_USB, +	CVMX_ERROR_GROUP_LMC, +	CVMX_ERROR_GROUP_ILK, +	CVMX_ERROR_GROUP_DFM, +	CVMX_ERROR_GROUP_ILA, +} cvmx_error_group_t; + +/** + * Flags representing special handling for some error registers. + * These flags are passed to cvmx_error_initialize() to control + * the handling of bits where the same flags were passed to the + * added cvmx_error_info_t. + */ +typedef enum { +	CVMX_ERROR_TYPE_NONE = 0, +	CVMX_ERROR_TYPE_SBE = 1 << 0, +	CVMX_ERROR_TYPE_DBE = 1 << 1, +} cvmx_error_type_t; + +/** + * When registering for interest in an error status register, the + * type of the register needs to be known by cvmx-error. Most + * registers are either IO64 or IO32, but some blocks contain + * registers that can't be directly accessed. A good example of + * would be PCIe extended error state stored in config space. + */ +typedef enum { +	__CVMX_ERROR_REGISTER_NONE, +	CVMX_ERROR_REGISTER_IO64, +	CVMX_ERROR_REGISTER_IO32, +	CVMX_ERROR_REGISTER_PCICONFIG, +	CVMX_ERROR_REGISTER_SRIOMAINT, +} cvmx_error_register_t; + +struct cvmx_error_info; +/** + * Error handling functions must have the following prototype. + */ +typedef int (*cvmx_error_func_t)(const struct cvmx_error_info *info); + +/** + * This structure is passed to all error handling functions. + */ +typedef struct cvmx_error_info { +	cvmx_error_register_t reg_type; +	u64 status_addr; +	u64 status_mask; +	u64 enable_addr; +	u64 enable_mask; +	cvmx_error_type_t flags; +	cvmx_error_group_t group; +	int group_index; +	cvmx_error_func_t func; +	u64 user_info; +	struct { +		cvmx_error_register_t reg_type; +		u64 status_addr; +		u64 status_mask; +	} parent; +} cvmx_error_info_t; + +/** + * Initialize the error status system. This should be called once + * before any other functions are called. This function adds default + * handlers for most all error events but does not enable them. Later + * calls to cvmx_error_enable() are needed. + * + * @param flags  Optional flags. + * + * @return Zero on success, negative on failure. + */ +int cvmx_error_initialize(void); + +/** + * Poll the error status registers and call the appropriate error + * handlers. This should be called in the RSL interrupt handler + * for your application or operating system. + * + * @return Number of error handlers called. Zero means this call + *         found no errors and was spurious. + */ +int cvmx_error_poll(void); + +/** + * Register to be called when an error status bit is set. Most users + * will not need to call this function as cvmx_error_initialize() + * registers default handlers for most error conditions. This function + * is normally used to add more handlers without changing the existing + * handlers. + * + * @param new_info Information about the handler for a error register. The + *                 structure passed is copied and can be destroyed after the + *                 call. All members of the structure must be populated, even the + *                 parent information. + * + * @return Zero on success, negative on failure. + */ +int cvmx_error_add(const cvmx_error_info_t *new_info); + +/** + * Remove all handlers for a status register and mask. Normally + * this function should not be called. Instead a new handler should be + * installed to replace the existing handler. In the even that all + * reporting of a error bit should be removed, then use this + * function. + * + * @param reg_type Type of the status register to remove + * @param status_addr + *                 Status register to remove. + * @param status_mask + *                 All handlers for this status register with this mask will be + *                 removed. + * @param old_info If not NULL, this is filled with information about the handler + *                 that was removed. + * + * @return Zero on success, negative on failure (not found). + */ +int cvmx_error_remove(cvmx_error_register_t reg_type, u64 status_addr, u64 status_mask, +		      cvmx_error_info_t *old_info); + +/** + * Change the function and user_info for an existing error status + * register. This function should be used to replace the default + * handler with an application specific version as needed. + * + * @param reg_type Type of the status register to change + * @param status_addr + *                 Status register to change. + * @param status_mask + *                 All handlers for this status register with this mask will be + *                 changed. + * @param new_func New function to use to handle the error status + * @param new_user_info + *                 New user info parameter for the function + * @param old_func If not NULL, the old function is returned. Useful for restoring + *                 the old handler. + * @param old_user_info + *                 If not NULL, the old user info parameter. + * + * @return Zero on success, negative on failure + */ +int cvmx_error_change_handler(cvmx_error_register_t reg_type, u64 status_addr, u64 status_mask, +			      cvmx_error_func_t new_func, u64 new_user_info, +			      cvmx_error_func_t *old_func, u64 *old_user_info); + +/** + * Enable all error registers for a logical group. This should be + * called whenever a logical group is brought online. + * + * @param group  Logical group to enable + * @param group_index + *               Index for the group as defined in the cvmx_error_group_t + *               comments. + * + * @return Zero on success, negative on failure. + */ +/* + * Rather than conditionalize the calls throughout the executive to not enable + * interrupts in Uboot, simply make the enable function do nothing + */ +static inline int cvmx_error_enable_group(cvmx_error_group_t group, int group_index) +{ +	return 0; +} + +/** + * Disable all error registers for a logical group. This should be + * called whenever a logical group is brought offline. Many blocks + * will report spurious errors when offline unless this function + * is called. + * + * @param group  Logical group to disable + * @param group_index + *               Index for the group as defined in the cvmx_error_group_t + *               comments. + * + * @return Zero on success, negative on failure. + */ +/* + * Rather than conditionalize the calls throughout the executive to not disable + * interrupts in Uboot, simply make the enable function do nothing + */ +static inline int cvmx_error_disable_group(cvmx_error_group_t group, int group_index) +{ +	return 0; +} + +/** + * Enable all handlers for a specific status register mask. + * + * @param reg_type Type of the status register + * @param status_addr + *                 Status register address + * @param status_mask + *                 All handlers for this status register with this mask will be + *                 enabled. + * + * @return Zero on success, negative on failure. + */ +int cvmx_error_enable(cvmx_error_register_t reg_type, u64 status_addr, u64 status_mask); + +/** + * Disable all handlers for a specific status register and mask. + * + * @param reg_type Type of the status register + * @param status_addr + *                 Status register address + * @param status_mask + *                 All handlers for this status register with this mask will be + *                 disabled. + * + * @return Zero on success, negative on failure. + */ +int cvmx_error_disable(cvmx_error_register_t reg_type, u64 status_addr, u64 status_mask); + +/** + * @INTERNAL + * Function for processing non leaf error status registers. This function + * calls all handlers for this passed register and all children linked + * to it. + * + * @param info   Error register to check + * + * @return Number of error status bits found or zero if no bits were set. + */ +int __cvmx_error_decode(const cvmx_error_info_t *info); + +/** + * @INTERNAL + * This error bit handler simply prints a message and clears the status bit + * + * @param info   Error register to check + * + * @return + */ +int __cvmx_error_display(const cvmx_error_info_t *info); + +/** + * Find the handler for a specific status register and mask + * + * @param status_addr + *                Status register address + * + * @return  Return the handler on success or null on failure. + */ +cvmx_error_info_t *cvmx_error_get_index(u64 status_addr); + +void __cvmx_install_gmx_error_handler_for_xaui(void); + +/** + * 78xx related + */ +/** + * Compare two INTSN values. + * + * @param key INTSN value to search for + * @param data current entry from the searched array + * + * @return Negative, 0 or positive when respectively key is less than, + *		equal or greater than data. + */ +int cvmx_error_intsn_cmp(const void *key, const void *data); + +/** + * @INTERNAL + * + * @param intsn   Interrupt source number to display + * + * @param node Node number + * + * @return Zero on success, -1 on error + */ +int cvmx_error_intsn_display_v3(int node, u32 intsn); + +/** + * Initialize the error status system for cn78xx. This should be called once + * before any other functions are called. This function enables the interrupts + * described in the array. + * + * @param node Node number + * + * @return Zero on success, negative on failure. + */ +int cvmx_error_initialize_cn78xx(int node); + +/** + * Enable interrupt for a specific INTSN. + * + * @param node Node number + * @param intsn Interrupt source number + * + * @return Zero on success, negative on failure. + */ +int cvmx_error_intsn_enable_v3(int node, u32 intsn); + +/** + * Disable interrupt for a specific INTSN. + * + * @param node Node number + * @param intsn Interrupt source number + * + * @return Zero on success, negative on failure. + */ +int cvmx_error_intsn_disable_v3(int node, u32 intsn); + +/** + * Clear interrupt for a specific INTSN. + * + * @param intsn Interrupt source number + * + * @return Zero on success, negative on failure. + */ +int cvmx_error_intsn_clear_v3(int node, u32 intsn); + +/** + * Enable interrupts for a specific CSR(all the bits/intsn in the csr). + * + * @param node Node number + * @param csr_address CSR address + * + * @return Zero on success, negative on failure. + */ +int cvmx_error_csr_enable_v3(int node, u64 csr_address); + +/** + * Disable interrupts for a specific CSR (all the bits/intsn in the csr). + * + * @param node Node number + * @param csr_address CSR address + * + * @return Zero + */ +int cvmx_error_csr_disable_v3(int node, u64 csr_address); + +/** + * Enable all error registers for a logical group. This should be + * called whenever a logical group is brought online. + * + * @param group  Logical group to enable + * @param xipd_port  The IPD port value + * + * @return Zero. + */ +int cvmx_error_enable_group_v3(cvmx_error_group_t group, int xipd_port); + +/** + * Disable all error registers for a logical group. + * + * @param group  Logical group to enable + * @param xipd_port  The IPD port value + * + * @return Zero. + */ +int cvmx_error_disable_group_v3(cvmx_error_group_t group, int xipd_port); + +/** + * Enable all error registers for a specific category in a logical group. + * This should be called whenever a logical group is brought online. + * + * @param group  Logical group to enable + * @param type   Category in a logical group to enable + * @param xipd_port  The IPD port value + * + * @return Zero. + */ +int cvmx_error_enable_group_type_v3(cvmx_error_group_t group, cvmx_error_type_t type, +				    int xipd_port); + +/** + * Disable all error registers for a specific category in a logical group. + * This should be called whenever a logical group is brought online. + * + * @param group  Logical group to disable + * @param type   Category in a logical group to disable + * @param xipd_port  The IPD port value + * + * @return Zero. + */ +int cvmx_error_disable_group_type_v3(cvmx_error_group_t group, cvmx_error_type_t type, +				     int xipd_port); + +/** + * Clear all error registers for a logical group. + * + * @param group  Logical group to disable + * @param xipd_port  The IPD port value + * + * @return Zero. + */ +int cvmx_error_clear_group_v3(cvmx_error_group_t group, int xipd_port); + +/** + * Enable all error registers for a particular category. + * + * @param node  CCPI node + * @param type  category to enable + * + *@return Zero. + */ +int cvmx_error_enable_type_v3(int node, cvmx_error_type_t type); + +/** + * Disable all error registers for a particular category. + * + * @param node  CCPI node + * @param type  category to disable + * + *@return Zero. + */ +int cvmx_error_disable_type_v3(int node, cvmx_error_type_t type); + +void cvmx_octeon_hang(void) __attribute__((__noreturn__)); + +/** + * @INTERNAL + * + * Process L2C single and multi-bit ECC errors + * + */ +int __cvmx_cn7xxx_l2c_l2d_ecc_error_display(int node, int intsn); + +/** + * Handle L2 cache TAG ECC errors and noway errors + * + * @param	CCPI node + * @param	intsn	intsn from error array. + * @param	remote	true for remote node (cn78xx only) + * + * @return	1 if handled, 0 if not handled + */ +int __cvmx_cn7xxx_l2c_tag_error_display(int node, int intsn, bool remote); + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-fpa.h b/arch/mips/mach-octeon/include/mach/cvmx-fpa.h new file mode 100644 index 00000000000..297fb3f4a28 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-fpa.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Interface to the hardware Free Pool Allocator. + */ + +#ifndef __CVMX_FPA_H__ +#define __CVMX_FPA_H__ + +#include "cvmx-scratch.h" +#include "cvmx-fpa-defs.h" +#include "cvmx-fpa1.h" +#include "cvmx-fpa3.h" + +#define CVMX_FPA_MIN_BLOCK_SIZE 128 +#define CVMX_FPA_ALIGNMENT	128 +#define CVMX_FPA_POOL_NAME_LEN	16 + +/* On CN78XX in backward-compatible mode, pool is mapped to AURA */ +#define CVMX_FPA_NUM_POOLS                                                                         \ +	(octeon_has_feature(OCTEON_FEATURE_FPA3) ? cvmx_fpa3_num_auras() : CVMX_FPA1_NUM_POOLS) + +/** + * Structure to store FPA pool configuration parameters. + */ +struct cvmx_fpa_pool_config { +	s64 pool_num; +	u64 buffer_size; +	u64 buffer_count; +}; + +typedef struct cvmx_fpa_pool_config cvmx_fpa_pool_config_t; + +/** + * Return the name of the pool + * + * @param pool_num   Pool to get the name of + * @return The name + */ +const char *cvmx_fpa_get_name(int pool_num); + +/** + * Initialize FPA per node + */ +int cvmx_fpa_global_init_node(int node); + +/** + * Enable the FPA + */ +static inline void cvmx_fpa_enable(void) +{ +	if (!octeon_has_feature(OCTEON_FEATURE_FPA3)) +		cvmx_fpa1_enable(); +	else +		cvmx_fpa_global_init_node(cvmx_get_node_num()); +} + +/** + * Disable the FPA + */ +static inline void cvmx_fpa_disable(void) +{ +	if (!octeon_has_feature(OCTEON_FEATURE_FPA3)) +		cvmx_fpa1_disable(); +	/* FPA3 does not have a disable function */ +} + +/** + * @INTERNAL + * @deprecated OBSOLETE + * + * Kept for transition assistance only + */ +static inline void cvmx_fpa_global_initialize(void) +{ +	cvmx_fpa_global_init_node(cvmx_get_node_num()); +} + +/** + * @INTERNAL + * + * Convert FPA1 style POOL into FPA3 AURA in + * backward compatibility mode. + */ +static inline cvmx_fpa3_gaura_t cvmx_fpa1_pool_to_fpa3_aura(cvmx_fpa1_pool_t pool) +{ +	if ((octeon_has_feature(OCTEON_FEATURE_FPA3))) { +		unsigned int node = cvmx_get_node_num(); +		cvmx_fpa3_gaura_t aura = __cvmx_fpa3_gaura(node, pool); +		return aura; +	} +	return CVMX_FPA3_INVALID_GAURA; +} + +/** + * Get a new block from the FPA + * + * @param pool   Pool to get the block from + * @return Pointer to the block or NULL on failure + */ +static inline void *cvmx_fpa_alloc(u64 pool) +{ +	/* FPA3 is handled differently */ +	if ((octeon_has_feature(OCTEON_FEATURE_FPA3))) { +		return cvmx_fpa3_alloc(cvmx_fpa1_pool_to_fpa3_aura(pool)); +	} else +		return cvmx_fpa1_alloc(pool); +} + +/** + * Asynchronously get a new block from the FPA + * + * The result of cvmx_fpa_async_alloc() may be retrieved using + * cvmx_fpa_async_alloc_finish(). + * + * @param scr_addr Local scratch address to put response in.  This is a byte + *		   address but must be 8 byte aligned. + * @param pool      Pool to get the block from + */ +static inline void cvmx_fpa_async_alloc(u64 scr_addr, u64 pool) +{ +	if ((octeon_has_feature(OCTEON_FEATURE_FPA3))) { +		return cvmx_fpa3_async_alloc(scr_addr, cvmx_fpa1_pool_to_fpa3_aura(pool)); +	} else +		return cvmx_fpa1_async_alloc(scr_addr, pool); +} + +/** + * Retrieve the result of cvmx_fpa_async_alloc + * + * @param scr_addr The Local scratch address.  Must be the same value + * passed to cvmx_fpa_async_alloc(). + * + * @param pool Pool the block came from.  Must be the same value + * passed to cvmx_fpa_async_alloc. + * + * @return Pointer to the block or NULL on failure + */ +static inline void *cvmx_fpa_async_alloc_finish(u64 scr_addr, u64 pool) +{ +	if ((octeon_has_feature(OCTEON_FEATURE_FPA3))) +		return cvmx_fpa3_async_alloc_finish(scr_addr, cvmx_fpa1_pool_to_fpa3_aura(pool)); +	else +		return cvmx_fpa1_async_alloc_finish(scr_addr, pool); +} + +/** + * Free a block allocated with a FPA pool. + * Does NOT provide memory ordering in cases where the memory block was + * modified by the core. + * + * @param ptr    Block to free + * @param pool   Pool to put it in + * @param num_cache_lines + *               Cache lines to invalidate + */ +static inline void cvmx_fpa_free_nosync(void *ptr, u64 pool, u64 num_cache_lines) +{ +	/* FPA3 is handled differently */ +	if ((octeon_has_feature(OCTEON_FEATURE_FPA3))) +		cvmx_fpa3_free_nosync(ptr, cvmx_fpa1_pool_to_fpa3_aura(pool), num_cache_lines); +	else +		cvmx_fpa1_free_nosync(ptr, pool, num_cache_lines); +} + +/** + * Free a block allocated with a FPA pool.  Provides required memory + * ordering in cases where memory block was modified by core. + * + * @param ptr    Block to free + * @param pool   Pool to put it in + * @param num_cache_lines + *               Cache lines to invalidate + */ +static inline void cvmx_fpa_free(void *ptr, u64 pool, u64 num_cache_lines) +{ +	if ((octeon_has_feature(OCTEON_FEATURE_FPA3))) +		cvmx_fpa3_free(ptr, cvmx_fpa1_pool_to_fpa3_aura(pool), num_cache_lines); +	else +		cvmx_fpa1_free(ptr, pool, num_cache_lines); +} + +/** + * Setup a FPA pool to control a new block of memory. + * This can only be called once per pool. Make sure proper + * locking enforces this. + * + * @param pool       Pool to initialize + * @param name       Constant character string to name this pool. + *                   String is not copied. + * @param buffer     Pointer to the block of memory to use. This must be + *                   accessible by all processors and external hardware. + * @param block_size Size for each block controlled by the FPA + * @param num_blocks Number of blocks + * + * @return the pool number on Success, + *         -1 on failure + */ +int cvmx_fpa_setup_pool(int pool, const char *name, void *buffer, u64 block_size, u64 num_blocks); + +int cvmx_fpa_shutdown_pool(int pool); + +/** + * Gets the block size of buffer in specified pool + * @param pool	 Pool to get the block size from + * @return       Size of buffer in specified pool + */ +unsigned int cvmx_fpa_get_block_size(int pool); + +int cvmx_fpa_is_pool_available(int pool_num); +u64 cvmx_fpa_get_pool_owner(int pool_num); +int cvmx_fpa_get_max_pools(void); +int cvmx_fpa_get_current_count(int pool_num); +int cvmx_fpa_validate_pool(int pool); + +#endif /*  __CVM_FPA_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-fpa1.h b/arch/mips/mach-octeon/include/mach/cvmx-fpa1.h new file mode 100644 index 00000000000..6985083a5d6 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-fpa1.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Interface to the hardware Free Pool Allocator on Octeon chips. + * These are the legacy models, i.e. prior to CN78XX/CN76XX. + */ + +#ifndef __CVMX_FPA1_HW_H__ +#define __CVMX_FPA1_HW_H__ + +#include "cvmx-scratch.h" +#include "cvmx-fpa-defs.h" +#include "cvmx-fpa3.h" + +/* Legacy pool range is 0..7 and 8 on CN68XX */ +typedef int cvmx_fpa1_pool_t; + +#define CVMX_FPA1_NUM_POOLS    8 +#define CVMX_FPA1_INVALID_POOL ((cvmx_fpa1_pool_t)-1) +#define CVMX_FPA1_NAME_SIZE    16 + +/** + * Structure describing the data format used for stores to the FPA. + */ +typedef union { +	u64 u64; +	struct { +		u64 scraddr : 8; +		u64 len : 8; +		u64 did : 8; +		u64 addr : 40; +	} s; +} cvmx_fpa1_iobdma_data_t; + +/* + * Allocate or reserve the specified fpa pool. + * + * @param pool	  FPA pool to allocate/reserve. If -1 it + *                finds an empty pool to allocate. + * @return        Alloctaed pool number or CVMX_FPA1_POOL_INVALID + *                if fails to allocate the pool + */ +cvmx_fpa1_pool_t cvmx_fpa1_reserve_pool(cvmx_fpa1_pool_t pool); + +/** + * Free the specified fpa pool. + * @param pool	   Pool to free + * @return         0 for success -1 failure + */ +int cvmx_fpa1_release_pool(cvmx_fpa1_pool_t pool); + +static inline void cvmx_fpa1_free(void *ptr, cvmx_fpa1_pool_t pool, u64 num_cache_lines) +{ +	cvmx_addr_t newptr; + +	newptr.u64 = cvmx_ptr_to_phys(ptr); +	newptr.sfilldidspace.didspace = CVMX_ADDR_DIDSPACE(CVMX_FULL_DID(CVMX_OCT_DID_FPA, pool)); +	/* Make sure that any previous writes to memory go out before we free +	 * this buffer.  This also serves as a barrier to prevent GCC from +	 * reordering operations to after the free. +	 */ +	CVMX_SYNCWS; +	/* value written is number of cache lines not written back */ +	cvmx_write_io(newptr.u64, num_cache_lines); +} + +static inline void cvmx_fpa1_free_nosync(void *ptr, cvmx_fpa1_pool_t pool, +					 unsigned int num_cache_lines) +{ +	cvmx_addr_t newptr; + +	newptr.u64 = cvmx_ptr_to_phys(ptr); +	newptr.sfilldidspace.didspace = CVMX_ADDR_DIDSPACE(CVMX_FULL_DID(CVMX_OCT_DID_FPA, pool)); +	/* Prevent GCC from reordering around free */ +	asm volatile("" : : : "memory"); +	/* value written is number of cache lines not written back */ +	cvmx_write_io(newptr.u64, num_cache_lines); +} + +/** + * Enable the FPA for use. Must be performed after any CSR + * configuration but before any other FPA functions. + */ +static inline void cvmx_fpa1_enable(void) +{ +	cvmx_fpa_ctl_status_t status; + +	status.u64 = csr_rd(CVMX_FPA_CTL_STATUS); +	if (status.s.enb) { +		/* +		 * CN68XXP1 should not reset the FPA (doing so may break +		 * the SSO, so we may end up enabling it more than once. +		 * Just return and don't spew messages. +		 */ +		return; +	} + +	status.u64 = 0; +	status.s.enb = 1; +	csr_wr(CVMX_FPA_CTL_STATUS, status.u64); +} + +/** + * Reset FPA to disable. Make sure buffers from all FPA pools are freed + * before disabling FPA. + */ +static inline void cvmx_fpa1_disable(void) +{ +	cvmx_fpa_ctl_status_t status; + +	if (OCTEON_IS_MODEL(OCTEON_CN68XX_PASS1)) +		return; + +	status.u64 = csr_rd(CVMX_FPA_CTL_STATUS); +	status.s.reset = 1; +	csr_wr(CVMX_FPA_CTL_STATUS, status.u64); +} + +static inline void *cvmx_fpa1_alloc(cvmx_fpa1_pool_t pool) +{ +	u64 address; + +	for (;;) { +		address = csr_rd(CVMX_ADDR_DID(CVMX_FULL_DID(CVMX_OCT_DID_FPA, pool))); +		if (cvmx_likely(address)) { +			return cvmx_phys_to_ptr(address); +		} else { +			if (csr_rd(CVMX_FPA_QUEX_AVAILABLE(pool)) > 0) +				udelay(50); +			else +				return NULL; +		} +	} +} + +/** + * Asynchronously get a new block from the FPA + * @INTERNAL + * + * The result of cvmx_fpa_async_alloc() may be retrieved using + * cvmx_fpa_async_alloc_finish(). + * + * @param scr_addr Local scratch address to put response in.  This is a byte + *		   address but must be 8 byte aligned. + * @param pool      Pool to get the block from + */ +static inline void cvmx_fpa1_async_alloc(u64 scr_addr, cvmx_fpa1_pool_t pool) +{ +	cvmx_fpa1_iobdma_data_t data; + +	/* Hardware only uses 64 bit aligned locations, so convert from byte +	 * address to 64-bit index +	 */ +	data.u64 = 0ull; +	data.s.scraddr = scr_addr >> 3; +	data.s.len = 1; +	data.s.did = CVMX_FULL_DID(CVMX_OCT_DID_FPA, pool); +	data.s.addr = 0; + +	cvmx_scratch_write64(scr_addr, 0ull); +	CVMX_SYNCW; +	cvmx_send_single(data.u64); +} + +/** + * Retrieve the result of cvmx_fpa_async_alloc + * @INTERNAL + * + * @param scr_addr The Local scratch address.  Must be the same value + * passed to cvmx_fpa_async_alloc(). + * + * @param pool Pool the block came from.  Must be the same value + * passed to cvmx_fpa_async_alloc. + * + * @return Pointer to the block or NULL on failure + */ +static inline void *cvmx_fpa1_async_alloc_finish(u64 scr_addr, cvmx_fpa1_pool_t pool) +{ +	u64 address; + +	CVMX_SYNCIOBDMA; + +	address = cvmx_scratch_read64(scr_addr); +	if (cvmx_likely(address)) +		return cvmx_phys_to_ptr(address); +	else +		return cvmx_fpa1_alloc(pool); +} + +static inline u64 cvmx_fpa1_get_available(cvmx_fpa1_pool_t pool) +{ +	return csr_rd(CVMX_FPA_QUEX_AVAILABLE(pool)); +} + +#endif /* __CVMX_FPA1_HW_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-fpa3.h b/arch/mips/mach-octeon/include/mach/cvmx-fpa3.h new file mode 100644 index 00000000000..229982b8316 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-fpa3.h @@ -0,0 +1,566 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Interface to the CN78XX Free Pool Allocator, a.k.a. FPA3 + */ + +#include "cvmx-address.h" +#include "cvmx-fpa-defs.h" +#include "cvmx-scratch.h" + +#ifndef __CVMX_FPA3_H__ +#define __CVMX_FPA3_H__ + +typedef struct { +	unsigned res0 : 6; +	unsigned node : 2; +	unsigned res1 : 2; +	unsigned lpool : 6; +	unsigned valid_magic : 16; +} cvmx_fpa3_pool_t; + +typedef struct { +	unsigned res0 : 6; +	unsigned node : 2; +	unsigned res1 : 6; +	unsigned laura : 10; +	unsigned valid_magic : 16; +} cvmx_fpa3_gaura_t; + +#define CVMX_FPA3_VALID_MAGIC	0xf9a3 +#define CVMX_FPA3_INVALID_GAURA ((cvmx_fpa3_gaura_t){ 0, 0, 0, 0, 0 }) +#define CVMX_FPA3_INVALID_POOL	((cvmx_fpa3_pool_t){ 0, 0, 0, 0, 0 }) + +static inline bool __cvmx_fpa3_aura_valid(cvmx_fpa3_gaura_t aura) +{ +	if (aura.valid_magic != CVMX_FPA3_VALID_MAGIC) +		return false; +	return true; +} + +static inline bool __cvmx_fpa3_pool_valid(cvmx_fpa3_pool_t pool) +{ +	if (pool.valid_magic != CVMX_FPA3_VALID_MAGIC) +		return false; +	return true; +} + +static inline cvmx_fpa3_gaura_t __cvmx_fpa3_gaura(int node, int laura) +{ +	cvmx_fpa3_gaura_t aura; + +	if (node < 0) +		node = cvmx_get_node_num(); +	if (laura < 0) +		return CVMX_FPA3_INVALID_GAURA; + +	aura.node = node; +	aura.laura = laura; +	aura.valid_magic = CVMX_FPA3_VALID_MAGIC; +	return aura; +} + +static inline cvmx_fpa3_pool_t __cvmx_fpa3_pool(int node, int lpool) +{ +	cvmx_fpa3_pool_t pool; + +	if (node < 0) +		node = cvmx_get_node_num(); +	if (lpool < 0) +		return CVMX_FPA3_INVALID_POOL; + +	pool.node = node; +	pool.lpool = lpool; +	pool.valid_magic = CVMX_FPA3_VALID_MAGIC; +	return pool; +} + +#undef CVMX_FPA3_VALID_MAGIC + +/** + * Structure describing the data format used for stores to the FPA. + */ +typedef union { +	u64 u64; +	struct { +		u64 scraddr : 8; +		u64 len : 8; +		u64 did : 8; +		u64 addr : 40; +	} s; +	struct { +		u64 scraddr : 8; +		u64 len : 8; +		u64 did : 8; +		u64 node : 4; +		u64 red : 1; +		u64 reserved2 : 9; +		u64 aura : 10; +		u64 reserved3 : 16; +	} cn78xx; +} cvmx_fpa3_iobdma_data_t; + +/** + * Struct describing load allocate operation addresses for FPA pool. + */ +union cvmx_fpa3_load_data { +	u64 u64; +	struct { +		u64 seg : 2; +		u64 reserved1 : 13; +		u64 io : 1; +		u64 did : 8; +		u64 node : 4; +		u64 red : 1; +		u64 reserved2 : 9; +		u64 aura : 10; +		u64 reserved3 : 16; +	}; +}; + +typedef union cvmx_fpa3_load_data cvmx_fpa3_load_data_t; + +/** + * Struct describing store free operation addresses from FPA pool. + */ +union cvmx_fpa3_store_addr { +	u64 u64; +	struct { +		u64 seg : 2; +		u64 reserved1 : 13; +		u64 io : 1; +		u64 did : 8; +		u64 node : 4; +		u64 reserved2 : 10; +		u64 aura : 10; +		u64 fabs : 1; +		u64 reserved3 : 3; +		u64 dwb_count : 9; +		u64 reserved4 : 3; +	}; +}; + +typedef union cvmx_fpa3_store_addr cvmx_fpa3_store_addr_t; + +enum cvmx_fpa3_pool_alignment_e { +	FPA_NATURAL_ALIGNMENT, +	FPA_OFFSET_ALIGNMENT, +	FPA_OPAQUE_ALIGNMENT +}; + +#define CVMX_FPA3_AURAX_LIMIT_MAX ((1ull << 40) - 1) + +/** + * @INTERNAL + * Accessor functions to return number of POOLS in an FPA3 + * depending on SoC model. + * The number is per-node for models supporting multi-node configurations. + */ +static inline int cvmx_fpa3_num_pools(void) +{ +	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) +		return 64; +	if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) +		return 32; +	if (OCTEON_IS_MODEL(OCTEON_CN73XX)) +		return 32; +	printf("ERROR: %s: Unknowm model\n", __func__); +	return -1; +} + +/** + * @INTERNAL + * Accessor functions to return number of AURAS in an FPA3 + * depending on SoC model. + * The number is per-node for models supporting multi-node configurations. + */ +static inline int cvmx_fpa3_num_auras(void) +{ +	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) +		return 1024; +	if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) +		return 512; +	if (OCTEON_IS_MODEL(OCTEON_CN73XX)) +		return 512; +	printf("ERROR: %s: Unknowm model\n", __func__); +	return -1; +} + +/** + * Get the FPA3 POOL underneath FPA3 AURA, containing all its buffers + * + */ +static inline cvmx_fpa3_pool_t cvmx_fpa3_aura_to_pool(cvmx_fpa3_gaura_t aura) +{ +	cvmx_fpa3_pool_t pool; +	cvmx_fpa_aurax_pool_t aurax_pool; + +	aurax_pool.u64 = cvmx_read_csr_node(aura.node, CVMX_FPA_AURAX_POOL(aura.laura)); + +	pool = __cvmx_fpa3_pool(aura.node, aurax_pool.s.pool); +	return pool; +} + +/** + * Get a new block from the FPA pool + * + * @param aura  - aura number + * @return pointer to the block or NULL on failure + */ +static inline void *cvmx_fpa3_alloc(cvmx_fpa3_gaura_t aura) +{ +	u64 address; +	cvmx_fpa3_load_data_t load_addr; + +	load_addr.u64 = 0; +	load_addr.seg = CVMX_MIPS_SPACE_XKPHYS; +	load_addr.io = 1; +	load_addr.did = 0x29; /* Device ID. Indicates FPA. */ +	load_addr.node = aura.node; +	load_addr.red = 0; /* Perform RED on allocation. +				  * FIXME to use config option +				  */ +	load_addr.aura = aura.laura; + +	address = cvmx_read64_uint64(load_addr.u64); +	if (!address) +		return NULL; +	return cvmx_phys_to_ptr(address); +} + +/** + * Asynchronously get a new block from the FPA + * + * The result of cvmx_fpa_async_alloc() may be retrieved using + * cvmx_fpa_async_alloc_finish(). + * + * @param scr_addr Local scratch address to put response in.  This is a byte + *		   address but must be 8 byte aligned. + * @param aura     Global aura to get the block from + */ +static inline void cvmx_fpa3_async_alloc(u64 scr_addr, cvmx_fpa3_gaura_t aura) +{ +	cvmx_fpa3_iobdma_data_t data; + +	/* Hardware only uses 64 bit aligned locations, so convert from byte +	 * address to 64-bit index +	 */ +	data.u64 = 0ull; +	data.cn78xx.scraddr = scr_addr >> 3; +	data.cn78xx.len = 1; +	data.cn78xx.did = 0x29; +	data.cn78xx.node = aura.node; +	data.cn78xx.aura = aura.laura; +	cvmx_scratch_write64(scr_addr, 0ull); + +	CVMX_SYNCW; +	cvmx_send_single(data.u64); +} + +/** + * Retrieve the result of cvmx_fpa3_async_alloc + * + * @param scr_addr The Local scratch address.  Must be the same value + * passed to cvmx_fpa_async_alloc(). + * + * @param aura Global aura the block came from.  Must be the same value + * passed to cvmx_fpa_async_alloc. + * + * @return Pointer to the block or NULL on failure + */ +static inline void *cvmx_fpa3_async_alloc_finish(u64 scr_addr, cvmx_fpa3_gaura_t aura) +{ +	u64 address; + +	CVMX_SYNCIOBDMA; + +	address = cvmx_scratch_read64(scr_addr); +	if (cvmx_likely(address)) +		return cvmx_phys_to_ptr(address); +	else +		/* Try regular alloc if async failed */ +		return cvmx_fpa3_alloc(aura); +} + +/** + * Free a pointer back to the pool. + * + * @param aura   global aura number + * @param ptr    physical address of block to free. + * @param num_cache_lines Cache lines to invalidate + */ +static inline void cvmx_fpa3_free(void *ptr, cvmx_fpa3_gaura_t aura, unsigned int num_cache_lines) +{ +	cvmx_fpa3_store_addr_t newptr; +	cvmx_addr_t newdata; + +	newdata.u64 = cvmx_ptr_to_phys(ptr); + +	/* Make sure that any previous writes to memory go out before we free +	   this buffer. This also serves as a barrier to prevent GCC from +	   reordering operations to after the free. */ +	CVMX_SYNCWS; + +	newptr.u64 = 0; +	newptr.seg = CVMX_MIPS_SPACE_XKPHYS; +	newptr.io = 1; +	newptr.did = 0x29; /* Device id, indicates FPA */ +	newptr.node = aura.node; +	newptr.aura = aura.laura; +	newptr.fabs = 0; /* Free absolute. FIXME to use config option */ +	newptr.dwb_count = num_cache_lines; + +	cvmx_write_io(newptr.u64, newdata.u64); +} + +/** + * Free a pointer back to the pool without flushing the write buffer. + * + * @param aura   global aura number + * @param ptr    physical address of block to free. + * @param num_cache_lines Cache lines to invalidate + */ +static inline void cvmx_fpa3_free_nosync(void *ptr, cvmx_fpa3_gaura_t aura, +					 unsigned int num_cache_lines) +{ +	cvmx_fpa3_store_addr_t newptr; +	cvmx_addr_t newdata; + +	newdata.u64 = cvmx_ptr_to_phys(ptr); + +	/* Prevent GCC from reordering writes to (*ptr) */ +	asm volatile("" : : : "memory"); + +	newptr.u64 = 0; +	newptr.seg = CVMX_MIPS_SPACE_XKPHYS; +	newptr.io = 1; +	newptr.did = 0x29; /* Device id, indicates FPA */ +	newptr.node = aura.node; +	newptr.aura = aura.laura; +	newptr.fabs = 0; /* Free absolute. FIXME to use config option */ +	newptr.dwb_count = num_cache_lines; + +	cvmx_write_io(newptr.u64, newdata.u64); +} + +static inline int cvmx_fpa3_pool_is_enabled(cvmx_fpa3_pool_t pool) +{ +	cvmx_fpa_poolx_cfg_t pool_cfg; + +	if (!__cvmx_fpa3_pool_valid(pool)) +		return -1; + +	pool_cfg.u64 = cvmx_read_csr_node(pool.node, CVMX_FPA_POOLX_CFG(pool.lpool)); +	return pool_cfg.cn78xx.ena; +} + +static inline int cvmx_fpa3_config_red_params(unsigned int node, int qos_avg_en, int red_lvl_dly, +					      int avg_dly) +{ +	cvmx_fpa_gen_cfg_t fpa_cfg; +	cvmx_fpa_red_delay_t red_delay; + +	fpa_cfg.u64 = cvmx_read_csr_node(node, CVMX_FPA_GEN_CFG); +	fpa_cfg.s.avg_en = qos_avg_en; +	fpa_cfg.s.lvl_dly = red_lvl_dly; +	cvmx_write_csr_node(node, CVMX_FPA_GEN_CFG, fpa_cfg.u64); + +	red_delay.u64 = cvmx_read_csr_node(node, CVMX_FPA_RED_DELAY); +	red_delay.s.avg_dly = avg_dly; +	cvmx_write_csr_node(node, CVMX_FPA_RED_DELAY, red_delay.u64); +	return 0; +} + +/** + * Gets the buffer size of the specified pool, + * + * @param aura Global aura number + * @return Returns size of the buffers in the specified pool. + */ +static inline int cvmx_fpa3_get_aura_buf_size(cvmx_fpa3_gaura_t aura) +{ +	cvmx_fpa3_pool_t pool; +	cvmx_fpa_poolx_cfg_t pool_cfg; +	int block_size; + +	pool = cvmx_fpa3_aura_to_pool(aura); + +	pool_cfg.u64 = cvmx_read_csr_node(pool.node, CVMX_FPA_POOLX_CFG(pool.lpool)); +	block_size = pool_cfg.cn78xx.buf_size << 7; +	return block_size; +} + +/** + * Return the number of available buffers in an AURA + * + * @param aura to receive count for + * @return available buffer count + */ +static inline long long cvmx_fpa3_get_available(cvmx_fpa3_gaura_t aura) +{ +	cvmx_fpa3_pool_t pool; +	cvmx_fpa_poolx_available_t avail_reg; +	cvmx_fpa_aurax_cnt_t cnt_reg; +	cvmx_fpa_aurax_cnt_limit_t limit_reg; +	long long ret; + +	pool = cvmx_fpa3_aura_to_pool(aura); + +	/* Get POOL available buffer count */ +	avail_reg.u64 = cvmx_read_csr_node(pool.node, CVMX_FPA_POOLX_AVAILABLE(pool.lpool)); + +	/* Get AURA current available count */ +	cnt_reg.u64 = cvmx_read_csr_node(aura.node, CVMX_FPA_AURAX_CNT(aura.laura)); +	limit_reg.u64 = cvmx_read_csr_node(aura.node, CVMX_FPA_AURAX_CNT_LIMIT(aura.laura)); + +	if (limit_reg.cn78xx.limit < cnt_reg.cn78xx.cnt) +		return 0; + +	/* Calculate AURA-based buffer allowance */ +	ret = limit_reg.cn78xx.limit - cnt_reg.cn78xx.cnt; + +	/* Use POOL real buffer availability when less then allowance */ +	if (ret > (long long)avail_reg.cn78xx.count) +		ret = avail_reg.cn78xx.count; + +	return ret; +} + +/** + * Configure the QoS parameters of an FPA3 AURA + * + * @param aura is the FPA3 AURA handle + * @param ena_bp enables backpressure when outstanding count exceeds 'bp_thresh' + * @param ena_red enables random early discard when outstanding count exceeds 'pass_thresh' + * @param pass_thresh is the maximum count to invoke flow control + * @param drop_thresh is the count threshold to begin dropping packets + * @param bp_thresh is the back-pressure threshold + * + */ +static inline void cvmx_fpa3_setup_aura_qos(cvmx_fpa3_gaura_t aura, bool ena_red, u64 pass_thresh, +					    u64 drop_thresh, bool ena_bp, u64 bp_thresh) +{ +	unsigned int shift = 0; +	u64 shift_thresh; +	cvmx_fpa_aurax_cnt_limit_t limit_reg; +	cvmx_fpa_aurax_cnt_levels_t aura_level; + +	if (!__cvmx_fpa3_aura_valid(aura)) +		return; + +	/* Get AURAX count limit for validation */ +	limit_reg.u64 = cvmx_read_csr_node(aura.node, CVMX_FPA_AURAX_CNT_LIMIT(aura.laura)); + +	if (pass_thresh < 256) +		pass_thresh = 255; + +	if (drop_thresh <= pass_thresh || drop_thresh > limit_reg.cn78xx.limit) +		drop_thresh = limit_reg.cn78xx.limit; + +	if (bp_thresh < 256 || bp_thresh > limit_reg.cn78xx.limit) +		bp_thresh = limit_reg.cn78xx.limit >> 1; + +	shift_thresh = (bp_thresh > drop_thresh) ? bp_thresh : drop_thresh; + +	/* Calculate shift so that the largest threshold fits in 8 bits */ +	for (shift = 0; shift < (1 << 6); shift++) { +		if (0 == ((shift_thresh >> shift) & ~0xffull)) +			break; +	}; + +	aura_level.u64 = cvmx_read_csr_node(aura.node, CVMX_FPA_AURAX_CNT_LEVELS(aura.laura)); +	aura_level.s.pass = pass_thresh >> shift; +	aura_level.s.drop = drop_thresh >> shift; +	aura_level.s.bp = bp_thresh >> shift; +	aura_level.s.shift = shift; +	aura_level.s.red_ena = ena_red; +	aura_level.s.bp_ena = ena_bp; +	cvmx_write_csr_node(aura.node, CVMX_FPA_AURAX_CNT_LEVELS(aura.laura), aura_level.u64); +} + +cvmx_fpa3_gaura_t cvmx_fpa3_reserve_aura(int node, int desired_aura_num); +int cvmx_fpa3_release_aura(cvmx_fpa3_gaura_t aura); +cvmx_fpa3_pool_t cvmx_fpa3_reserve_pool(int node, int desired_pool_num); +int cvmx_fpa3_release_pool(cvmx_fpa3_pool_t pool); +int cvmx_fpa3_is_aura_available(int node, int aura_num); +int cvmx_fpa3_is_pool_available(int node, int pool_num); + +cvmx_fpa3_pool_t cvmx_fpa3_setup_fill_pool(int node, int desired_pool, const char *name, +					   unsigned int block_size, unsigned int num_blocks, +					   void *buffer); + +/** + * Function to attach an aura to an existing pool + * + * @param node - configure fpa on this node + * @param pool - configured pool to attach aura to + * @param desired_aura - pointer to aura to use, set to -1 to allocate + * @param name - name to register + * @param block_size - size of buffers to use + * @param num_blocks - number of blocks to allocate + * + * @return configured gaura on success, CVMX_FPA3_INVALID_GAURA on failure + */ +cvmx_fpa3_gaura_t cvmx_fpa3_set_aura_for_pool(cvmx_fpa3_pool_t pool, int desired_aura, +					      const char *name, unsigned int block_size, +					      unsigned int num_blocks); + +/** + * Function to setup and initialize a pool. + * + * @param node - configure fpa on this node + * @param desired_aura - aura to use, -1 for dynamic allocation + * @param name - name to register + * @param block_size - size of buffers in pool + * @param num_blocks - max number of buffers allowed + */ +cvmx_fpa3_gaura_t cvmx_fpa3_setup_aura_and_pool(int node, int desired_aura, const char *name, +						void *buffer, unsigned int block_size, +						unsigned int num_blocks); + +int cvmx_fpa3_shutdown_aura_and_pool(cvmx_fpa3_gaura_t aura); +int cvmx_fpa3_shutdown_aura(cvmx_fpa3_gaura_t aura); +int cvmx_fpa3_shutdown_pool(cvmx_fpa3_pool_t pool); +const char *cvmx_fpa3_get_pool_name(cvmx_fpa3_pool_t pool); +int cvmx_fpa3_get_pool_buf_size(cvmx_fpa3_pool_t pool); +const char *cvmx_fpa3_get_aura_name(cvmx_fpa3_gaura_t aura); + +/* FIXME: Need a different macro for stage2 of u-boot */ + +static inline void cvmx_fpa3_stage2_init(int aura, int pool, u64 stack_paddr, int stacklen, +					 int buffer_sz, int buf_cnt) +{ +	cvmx_fpa_poolx_cfg_t pool_cfg; + +	/* Configure pool stack */ +	cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_BASE(pool), stack_paddr); +	cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_ADDR(pool), stack_paddr); +	cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_END(pool), stack_paddr + stacklen); + +	/* Configure pool with buffer size */ +	pool_cfg.u64 = 0; +	pool_cfg.cn78xx.nat_align = 1; +	pool_cfg.cn78xx.buf_size = buffer_sz >> 7; +	pool_cfg.cn78xx.l_type = 0x2; +	pool_cfg.cn78xx.ena = 0; +	cvmx_write_csr_node(0, CVMX_FPA_POOLX_CFG(pool), pool_cfg.u64); +	/* Reset pool before starting */ +	pool_cfg.cn78xx.ena = 1; +	cvmx_write_csr_node(0, CVMX_FPA_POOLX_CFG(pool), pool_cfg.u64); + +	cvmx_write_csr_node(0, CVMX_FPA_AURAX_CFG(aura), 0); +	cvmx_write_csr_node(0, CVMX_FPA_AURAX_CNT_ADD(aura), buf_cnt); +	cvmx_write_csr_node(0, CVMX_FPA_AURAX_POOL(aura), (u64)pool); +} + +static inline void cvmx_fpa3_stage2_disable(int aura, int pool) +{ +	cvmx_write_csr_node(0, CVMX_FPA_AURAX_POOL(aura), 0); +	cvmx_write_csr_node(0, CVMX_FPA_POOLX_CFG(pool), 0); +	cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_BASE(pool), 0); +	cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_ADDR(pool), 0); +	cvmx_write_csr_node(0, CVMX_FPA_POOLX_STACK_END(pool), 0); +} + +#endif /* __CVMX_FPA3_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-global-resources.h b/arch/mips/mach-octeon/include/mach/cvmx-global-resources.h new file mode 100644 index 00000000000..28c32ddbe17 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-global-resources.h @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + */ + +#ifndef _CVMX_GLOBAL_RESOURCES_T_ +#define _CVMX_GLOBAL_RESOURCES_T_ + +#define CVMX_GLOBAL_RESOURCES_DATA_NAME "cvmx-global-resources" + +/*In macros below abbreviation GR stands for global resources. */ +#define CVMX_GR_TAG_INVALID                                                                        \ +	cvmx_get_gr_tag('i', 'n', 'v', 'a', 'l', 'i', 'd', '.', '.', '.', '.', '.', '.', '.', '.', \ +			'.') +/*Tag for pko que table range. */ +#define CVMX_GR_TAG_PKO_QUEUES                                                                     \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'p', 'k', 'o', '_', 'q', 'u', 'e', 'u', 's', '.', '.', \ +			'.') +/*Tag for a pko internal ports range */ +#define CVMX_GR_TAG_PKO_IPORTS                                                                     \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'p', 'k', 'o', '_', 'i', 'p', 'o', 'r', 't', '.', '.', \ +			'.') +#define CVMX_GR_TAG_FPA                                                                            \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'f', 'p', 'a', '.', '.', '.', '.', '.', '.', '.', '.', \ +			'.') +#define CVMX_GR_TAG_FAU                                                                            \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'f', 'a', 'u', '.', '.', '.', '.', '.', '.', '.', '.', \ +			'.') +#define CVMX_GR_TAG_SSO_GRP(n)                                                                     \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 's', 's', 'o', '_', '0', (n) + '0', '.', '.', '.',     \ +			'.', '.', '.'); +#define CVMX_GR_TAG_TIM(n)                                                                         \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 't', 'i', 'm', '_', (n) + '0', '.', '.', '.', '.',     \ +			'.', '.', '.') +#define CVMX_GR_TAG_CLUSTERS(x)                                                                    \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'c', 'l', 'u', 's', 't', 'e', 'r', '_', (x + '0'),     \ +			'.', '.', '.') +#define CVMX_GR_TAG_CLUSTER_GRP(x)                                                                 \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'c', 'l', 'g', 'r', 'p', '_', (x + '0'), '.', '.',     \ +			'.', '.', '.') +#define CVMX_GR_TAG_STYLE(x)                                                                       \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 's', 't', 'y', 'l', 'e', '_', (x + '0'), '.', '.',     \ +			'.', '.', '.') +#define CVMX_GR_TAG_QPG_ENTRY(x)                                                                   \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'q', 'p', 'g', 'e', 't', '_', (x + '0'), '.', '.',     \ +			'.', '.', '.') +#define CVMX_GR_TAG_BPID(x)                                                                        \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'b', 'p', 'i', 'd', 's', '_', (x + '0'), '.', '.',     \ +			'.', '.', '.') +#define CVMX_GR_TAG_MTAG_IDX(x)                                                                    \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'm', 't', 'a', 'g', 'x', '_', (x + '0'), '.', '.',     \ +			'.', '.', '.') +#define CVMX_GR_TAG_PCAM(x, y, z)                                                                  \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'p', 'c', 'a', 'm', '_', (x + '0'), (y + '0'),         \ +			(z + '0'), '.', '.', '.', '.') + +#define CVMX_GR_TAG_CIU3_IDT(_n)                                                                   \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'c', 'i', 'u', '3', '_', ((_n) + '0'), '_', 'i', 'd',  \ +			't', '.', '.') + +/* Allocation of the 512 SW INTSTs (in the  12 bit SW INTSN space) */ +#define CVMX_GR_TAG_CIU3_SWINTSN(_n)                                                               \ +	cvmx_get_gr_tag('c', 'v', 'm', '_', 'c', 'i', 'u', '3', '_', ((_n) + '0'), '_', 's', 'w',  \ +			'i', 's', 'n') + +#define TAG_INIT_PART(A, B, C, D, E, F, G, H)                                                      \ +	((((u64)(A) & 0xff) << 56) | (((u64)(B) & 0xff) << 48) | (((u64)(C) & 0xff) << 40) |             \ +	 (((u64)(D) & 0xff) << 32) | (((u64)(E) & 0xff) << 24) | (((u64)(F) & 0xff) << 16) |             \ +	 (((u64)(G) & 0xff) << 8) | (((u64)(H) & 0xff))) + +struct global_resource_tag { +	u64 lo; +	u64 hi; +}; + +enum cvmx_resource_err { CVMX_RESOURCE_ALLOC_FAILED = -1, CVMX_RESOURCE_ALREADY_RESERVED = -2 }; + +/* + * @INTERNAL + * Creates a tag from the specified characters. + */ +static inline struct global_resource_tag cvmx_get_gr_tag(char a, char b, char c, char d, char e, +							 char f, char g, char h, char i, char j, +							 char k, char l, char m, char n, char o, +							 char p) +{ +	struct global_resource_tag tag; + +	tag.lo = TAG_INIT_PART(a, b, c, d, e, f, g, h); +	tag.hi = TAG_INIT_PART(i, j, k, l, m, n, o, p); +	return tag; +} + +static inline int cvmx_gr_same_tag(struct global_resource_tag gr1, struct global_resource_tag gr2) +{ +	return (gr1.hi == gr2.hi) && (gr1.lo == gr2.lo); +} + +/* + * @INTERNAL + * Creates a global resource range that can hold the specified number of + * elements + * @param tag is the tag of the range. The taga is created using the method + * cvmx_get_gr_tag() + * @param nelements is the number of elements to be held in the resource range. + */ +int cvmx_create_global_resource_range(struct global_resource_tag tag, int nelements); + +/* + * @INTERNAL + * Allocate nelements in the global resource range with the specified tag. It + * is assumed that prior + * to calling this the global resource range has already been created using + * cvmx_create_global_resource_range(). + * @param tag is the tag of the global resource range. + * @param nelements is the number of elements to be allocated. + * @param owner is a 64 bit number that identifes the owner of this range. + * @aligment specifes the required alignment of the returned base number. + * @return returns the base of the allocated range. -1 return value indicates + * failure. + */ +int cvmx_allocate_global_resource_range(struct global_resource_tag tag, u64 owner, int nelements, +					int alignment); + +/* + * @INTERNAL + * Allocate nelements in the global resource range with the specified tag. + * The elements allocated need not be contiguous. It is assumed that prior to + * calling this the global resource range has already + * been created using cvmx_create_global_resource_range(). + * @param tag is the tag of the global resource range. + * @param nelements is the number of elements to be allocated. + * @param owner is a 64 bit number that identifes the owner of the allocated + * elements. + * @param allocated_elements returns indexs of the allocated entries. + * @return returns 0 on success and -1 on failure. + */ +int cvmx_resource_alloc_many(struct global_resource_tag tag, u64 owner, int nelements, +			     int allocated_elements[]); +int cvmx_resource_alloc_reverse(struct global_resource_tag, u64 owner); +/* + * @INTERNAL + * Reserve nelements starting from base in the global resource range with the + * specified tag. + * It is assumed that prior to calling this the global resource range has + * already been created using cvmx_create_global_resource_range(). + * @param tag is the tag of the global resource range. + * @param nelements is the number of elements to be allocated. + * @param owner is a 64 bit number that identifes the owner of this range. + * @base specifies the base start of nelements. + * @return returns the base of the allocated range. -1 return value indicates + * failure. + */ +int cvmx_reserve_global_resource_range(struct global_resource_tag tag, u64 owner, int base, +				       int nelements); +/* + * @INTERNAL + * Free nelements starting at base in the global resource range with the + * specified tag. + * @param tag is the tag of the global resource range. + * @param base is the base number + * @param nelements is the number of elements that are to be freed. + * @return returns 0 if successful and -1 on failure. + */ +int cvmx_free_global_resource_range_with_base(struct global_resource_tag tag, int base, +					      int nelements); + +/* + * @INTERNAL + * Free nelements with the bases specified in bases[] with the + * specified tag. + * @param tag is the tag of the global resource range. + * @param bases is an array containing the bases to be freed. + * @param nelements is the number of elements that are to be freed. + * @return returns 0 if successful and -1 on failure. + */ +int cvmx_free_global_resource_range_multiple(struct global_resource_tag tag, int bases[], +					     int nelements); +/* + * @INTERNAL + * Free elements from the specified owner in the global resource range with the + * specified tag. + * @param tag is the tag of the global resource range. + * @param owner is the owner of resources that are to be freed. + * @return returns 0 if successful and -1 on failure. + */ +int cvmx_free_global_resource_range_with_owner(struct global_resource_tag tag, int owner); + +/* + * @INTERNAL + * Frees all the global resources that have been created. + * For use only from the bootloader, when it shutdown and boots up the + * application or kernel. + */ +int free_global_resources(void); + +u64 cvmx_get_global_resource_owner(struct global_resource_tag tag, int base); +/* + * @INTERNAL + * Shows the global resource range with the specified tag. Use mainly for debug. + */ +void cvmx_show_global_resource_range(struct global_resource_tag tag); + +/* + * @INTERNAL + * Shows all the global resources. Used mainly for debug. + */ +void cvmx_global_resources_show(void); + +u64 cvmx_allocate_app_id(void); +u64 cvmx_get_app_id(void); + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-gmx.h b/arch/mips/mach-octeon/include/mach/cvmx-gmx.h new file mode 100644 index 00000000000..2df7da102a0 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-gmx.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Interface to the GMX hardware. + */ + +#ifndef __CVMX_GMX_H__ +#define __CVMX_GMX_H__ + +/* CSR typedefs have been moved to cvmx-gmx-defs.h */ + +int cvmx_gmx_set_backpressure_override(u32 interface, u32 port_mask); +int cvmx_agl_set_backpressure_override(u32 interface, u32 port_mask); + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-hwfau.h b/arch/mips/mach-octeon/include/mach/cvmx-hwfau.h new file mode 100644 index 00000000000..59772190aa3 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-hwfau.h @@ -0,0 +1,606 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Interface to the hardware Fetch and Add Unit. + */ + +/** + * @file + * + * Interface to the hardware Fetch and Add Unit. + * + */ + +#ifndef __CVMX_HWFAU_H__ +#define __CVMX_HWFAU_H__ + +typedef int cvmx_fau_reg64_t; +typedef int cvmx_fau_reg32_t; +typedef int cvmx_fau_reg16_t; +typedef int cvmx_fau_reg8_t; + +#define CVMX_FAU_REG_ANY -1 + +/* + * Octeon Fetch and Add Unit (FAU) + */ + +#define CVMX_FAU_LOAD_IO_ADDRESS cvmx_build_io_address(0x1e, 0) +#define CVMX_FAU_BITS_SCRADDR	 63, 56 +#define CVMX_FAU_BITS_LEN	 55, 48 +#define CVMX_FAU_BITS_INEVAL	 35, 14 +#define CVMX_FAU_BITS_TAGWAIT	 13, 13 +#define CVMX_FAU_BITS_NOADD	 13, 13 +#define CVMX_FAU_BITS_SIZE	 12, 11 +#define CVMX_FAU_BITS_REGISTER	 10, 0 + +#define CVMX_FAU_MAX_REGISTERS_8 (2048) + +typedef enum { +	CVMX_FAU_OP_SIZE_8 = 0, +	CVMX_FAU_OP_SIZE_16 = 1, +	CVMX_FAU_OP_SIZE_32 = 2, +	CVMX_FAU_OP_SIZE_64 = 3 +} cvmx_fau_op_size_t; + +/** + * Tagwait return definition. If a timeout occurs, the error + * bit will be set. Otherwise the value of the register before + * the update will be returned. + */ +typedef struct { +	u64 error : 1; +	s64 value : 63; +} cvmx_fau_tagwait64_t; + +/** + * Tagwait return definition. If a timeout occurs, the error + * bit will be set. Otherwise the value of the register before + * the update will be returned. + */ +typedef struct { +	u64 error : 1; +	s32 value : 31; +} cvmx_fau_tagwait32_t; + +/** + * Tagwait return definition. If a timeout occurs, the error + * bit will be set. Otherwise the value of the register before + * the update will be returned. + */ +typedef struct { +	u64 error : 1; +	s16 value : 15; +} cvmx_fau_tagwait16_t; + +/** + * Tagwait return definition. If a timeout occurs, the error + * bit will be set. Otherwise the value of the register before + * the update will be returned. + */ +typedef struct { +	u64 error : 1; +	int8_t value : 7; +} cvmx_fau_tagwait8_t; + +/** + * Asynchronous tagwait return definition. If a timeout occurs, + * the error bit will be set. Otherwise the value of the + * register before the update will be returned. + */ +typedef union { +	u64 u64; +	struct { +		u64 invalid : 1; +		u64 data : 63; /* unpredictable if invalid is set */ +	} s; +} cvmx_fau_async_tagwait_result_t; + +#define SWIZZLE_8  0 +#define SWIZZLE_16 0 +#define SWIZZLE_32 0 + +/** + * @INTERNAL + * Builds a store I/O address for writing to the FAU + * + * @param noadd  0 = Store value is atomically added to the current value + *               1 = Store value is atomically written over the current value + * @param reg    FAU atomic register to access. 0 <= reg < 2048. + *               - Step by 2 for 16 bit access. + *               - Step by 4 for 32 bit access. + *               - Step by 8 for 64 bit access. + * @return Address to store for atomic update + */ +static inline u64 __cvmx_hwfau_store_address(u64 noadd, u64 reg) +{ +	return (CVMX_ADD_IO_SEG(CVMX_FAU_LOAD_IO_ADDRESS) | +		cvmx_build_bits(CVMX_FAU_BITS_NOADD, noadd) | +		cvmx_build_bits(CVMX_FAU_BITS_REGISTER, reg)); +} + +/** + * @INTERNAL + * Builds a I/O address for accessing the FAU + * + * @param tagwait Should the atomic add wait for the current tag switch + *                operation to complete. + *                - 0 = Don't wait + *                - 1 = Wait for tag switch to complete + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 2 for 16 bit access. + *                - Step by 4 for 32 bit access. + *                - Step by 8 for 64 bit access. + * @param value   Signed value to add. + *                Note: When performing 32 and 64 bit access, only the low + *                22 bits are available. + * @return Address to read from for atomic update + */ +static inline u64 __cvmx_hwfau_atomic_address(u64 tagwait, u64 reg, s64 value) +{ +	return (CVMX_ADD_IO_SEG(CVMX_FAU_LOAD_IO_ADDRESS) | +		cvmx_build_bits(CVMX_FAU_BITS_INEVAL, value) | +		cvmx_build_bits(CVMX_FAU_BITS_TAGWAIT, tagwait) | +		cvmx_build_bits(CVMX_FAU_BITS_REGISTER, reg)); +} + +/** + * Perform an atomic 64 bit add + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 8 for 64 bit access. + * @param value   Signed value to add. + *                Note: Only the low 22 bits are available. + * @return Value of the register before the update + */ +static inline s64 cvmx_hwfau_fetch_and_add64(cvmx_fau_reg64_t reg, s64 value) +{ +	return cvmx_read64_int64(__cvmx_hwfau_atomic_address(0, reg, value)); +} + +/** + * Perform an atomic 32 bit add + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 4 for 32 bit access. + * @param value   Signed value to add. + *                Note: Only the low 22 bits are available. + * @return Value of the register before the update + */ +static inline s32 cvmx_hwfau_fetch_and_add32(cvmx_fau_reg32_t reg, s32 value) +{ +	reg ^= SWIZZLE_32; +	return cvmx_read64_int32(__cvmx_hwfau_atomic_address(0, reg, value)); +} + +/** + * Perform an atomic 16 bit add + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 2 for 16 bit access. + * @param value   Signed value to add. + * @return Value of the register before the update + */ +static inline s16 cvmx_hwfau_fetch_and_add16(cvmx_fau_reg16_t reg, s16 value) +{ +	reg ^= SWIZZLE_16; +	return cvmx_read64_int16(__cvmx_hwfau_atomic_address(0, reg, value)); +} + +/** + * Perform an atomic 8 bit add + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + * @param value   Signed value to add. + * @return Value of the register before the update + */ +static inline int8_t cvmx_hwfau_fetch_and_add8(cvmx_fau_reg8_t reg, int8_t value) +{ +	reg ^= SWIZZLE_8; +	return cvmx_read64_int8(__cvmx_hwfau_atomic_address(0, reg, value)); +} + +/** + * Perform an atomic 64 bit add after the current tag switch + * completes + * + * @param reg    FAU atomic register to access. 0 <= reg < 2048. + *               - Step by 8 for 64 bit access. + * @param value  Signed value to add. + *               Note: Only the low 22 bits are available. + * @return If a timeout occurs, the error bit will be set. Otherwise + *         the value of the register before the update will be + *         returned + */ +static inline cvmx_fau_tagwait64_t cvmx_hwfau_tagwait_fetch_and_add64(cvmx_fau_reg64_t reg, +								      s64 value) +{ +	union { +		u64 i64; +		cvmx_fau_tagwait64_t t; +	} result; +	result.i64 = cvmx_read64_int64(__cvmx_hwfau_atomic_address(1, reg, value)); +	return result.t; +} + +/** + * Perform an atomic 32 bit add after the current tag switch + * completes + * + * @param reg    FAU atomic register to access. 0 <= reg < 2048. + *               - Step by 4 for 32 bit access. + * @param value  Signed value to add. + *               Note: Only the low 22 bits are available. + * @return If a timeout occurs, the error bit will be set. Otherwise + *         the value of the register before the update will be + *         returned + */ +static inline cvmx_fau_tagwait32_t cvmx_hwfau_tagwait_fetch_and_add32(cvmx_fau_reg32_t reg, +								      s32 value) +{ +	union { +		u64 i32; +		cvmx_fau_tagwait32_t t; +	} result; +	reg ^= SWIZZLE_32; +	result.i32 = cvmx_read64_int32(__cvmx_hwfau_atomic_address(1, reg, value)); +	return result.t; +} + +/** + * Perform an atomic 16 bit add after the current tag switch + * completes + * + * @param reg    FAU atomic register to access. 0 <= reg < 2048. + *               - Step by 2 for 16 bit access. + * @param value  Signed value to add. + * @return If a timeout occurs, the error bit will be set. Otherwise + *         the value of the register before the update will be + *         returned + */ +static inline cvmx_fau_tagwait16_t cvmx_hwfau_tagwait_fetch_and_add16(cvmx_fau_reg16_t reg, +								      s16 value) +{ +	union { +		u64 i16; +		cvmx_fau_tagwait16_t t; +	} result; +	reg ^= SWIZZLE_16; +	result.i16 = cvmx_read64_int16(__cvmx_hwfau_atomic_address(1, reg, value)); +	return result.t; +} + +/** + * Perform an atomic 8 bit add after the current tag switch + * completes + * + * @param reg    FAU atomic register to access. 0 <= reg < 2048. + * @param value  Signed value to add. + * @return If a timeout occurs, the error bit will be set. Otherwise + *         the value of the register before the update will be + *         returned + */ +static inline cvmx_fau_tagwait8_t cvmx_hwfau_tagwait_fetch_and_add8(cvmx_fau_reg8_t reg, +								    int8_t value) +{ +	union { +		u64 i8; +		cvmx_fau_tagwait8_t t; +	} result; +	reg ^= SWIZZLE_8; +	result.i8 = cvmx_read64_int8(__cvmx_hwfau_atomic_address(1, reg, value)); +	return result.t; +} + +/** + * @INTERNAL + * Builds I/O data for async operations + * + * @param scraddr Scratch pad byte address to write to.  Must be 8 byte aligned + * @param value   Signed value to add. + *                Note: When performing 32 and 64 bit access, only the low + *                22 bits are available. + * @param tagwait Should the atomic add wait for the current tag switch + *                operation to complete. + *                - 0 = Don't wait + *                - 1 = Wait for tag switch to complete + * @param size    The size of the operation: + *                - CVMX_FAU_OP_SIZE_8  (0) = 8 bits + *                - CVMX_FAU_OP_SIZE_16 (1) = 16 bits + *                - CVMX_FAU_OP_SIZE_32 (2) = 32 bits + *                - CVMX_FAU_OP_SIZE_64 (3) = 64 bits + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 2 for 16 bit access. + *                - Step by 4 for 32 bit access. + *                - Step by 8 for 64 bit access. + * @return Data to write using cvmx_send_single + */ +static inline u64 __cvmx_fau_iobdma_data(u64 scraddr, s64 value, u64 tagwait, +					 cvmx_fau_op_size_t size, u64 reg) +{ +	return (CVMX_FAU_LOAD_IO_ADDRESS | cvmx_build_bits(CVMX_FAU_BITS_SCRADDR, scraddr >> 3) | +		cvmx_build_bits(CVMX_FAU_BITS_LEN, 1) | +		cvmx_build_bits(CVMX_FAU_BITS_INEVAL, value) | +		cvmx_build_bits(CVMX_FAU_BITS_TAGWAIT, tagwait) | +		cvmx_build_bits(CVMX_FAU_BITS_SIZE, size) | +		cvmx_build_bits(CVMX_FAU_BITS_REGISTER, reg)); +} + +/** + * Perform an async atomic 64 bit add. The old value is + * placed in the scratch memory at byte address scraddr. + * + * @param scraddr Scratch memory byte address to put response in. + *                Must be 8 byte aligned. + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 8 for 64 bit access. + * @param value   Signed value to add. + *                Note: Only the low 22 bits are available. + * @return Placed in the scratch pad register + */ +static inline void cvmx_hwfau_async_fetch_and_add64(u64 scraddr, cvmx_fau_reg64_t reg, s64 value) +{ +	cvmx_send_single(__cvmx_fau_iobdma_data(scraddr, value, 0, CVMX_FAU_OP_SIZE_64, reg)); +} + +/** + * Perform an async atomic 32 bit add. The old value is + * placed in the scratch memory at byte address scraddr. + * + * @param scraddr Scratch memory byte address to put response in. + *                Must be 8 byte aligned. + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 4 for 32 bit access. + * @param value   Signed value to add. + *                Note: Only the low 22 bits are available. + * @return Placed in the scratch pad register + */ +static inline void cvmx_hwfau_async_fetch_and_add32(u64 scraddr, cvmx_fau_reg32_t reg, s32 value) +{ +	cvmx_send_single(__cvmx_fau_iobdma_data(scraddr, value, 0, CVMX_FAU_OP_SIZE_32, reg)); +} + +/** + * Perform an async atomic 16 bit add. The old value is + * placed in the scratch memory at byte address scraddr. + * + * @param scraddr Scratch memory byte address to put response in. + *                Must be 8 byte aligned. + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 2 for 16 bit access. + * @param value   Signed value to add. + * @return Placed in the scratch pad register + */ +static inline void cvmx_hwfau_async_fetch_and_add16(u64 scraddr, cvmx_fau_reg16_t reg, s16 value) +{ +	cvmx_send_single(__cvmx_fau_iobdma_data(scraddr, value, 0, CVMX_FAU_OP_SIZE_16, reg)); +} + +/** + * Perform an async atomic 8 bit add. The old value is + * placed in the scratch memory at byte address scraddr. + * + * @param scraddr Scratch memory byte address to put response in. + *                Must be 8 byte aligned. + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + * @param value   Signed value to add. + * @return Placed in the scratch pad register + */ +static inline void cvmx_hwfau_async_fetch_and_add8(u64 scraddr, cvmx_fau_reg8_t reg, int8_t value) +{ +	cvmx_send_single(__cvmx_fau_iobdma_data(scraddr, value, 0, CVMX_FAU_OP_SIZE_8, reg)); +} + +/** + * Perform an async atomic 64 bit add after the current tag + * switch completes. + * + * @param scraddr Scratch memory byte address to put response in. + *                Must be 8 byte aligned. + *                If a timeout occurs, the error bit (63) will be set. Otherwise + *                the value of the register before the update will be + *                returned + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 8 for 64 bit access. + * @param value   Signed value to add. + *                Note: Only the low 22 bits are available. + * @return Placed in the scratch pad register + */ +static inline void cvmx_hwfau_async_tagwait_fetch_and_add64(u64 scraddr, cvmx_fau_reg64_t reg, +							    s64 value) +{ +	cvmx_send_single(__cvmx_fau_iobdma_data(scraddr, value, 1, CVMX_FAU_OP_SIZE_64, reg)); +} + +/** + * Perform an async atomic 32 bit add after the current tag + * switch completes. + * + * @param scraddr Scratch memory byte address to put response in. + *                Must be 8 byte aligned. + *                If a timeout occurs, the error bit (63) will be set. Otherwise + *                the value of the register before the update will be + *                returned + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 4 for 32 bit access. + * @param value   Signed value to add. + *                Note: Only the low 22 bits are available. + * @return Placed in the scratch pad register + */ +static inline void cvmx_hwfau_async_tagwait_fetch_and_add32(u64 scraddr, cvmx_fau_reg32_t reg, +							    s32 value) +{ +	cvmx_send_single(__cvmx_fau_iobdma_data(scraddr, value, 1, CVMX_FAU_OP_SIZE_32, reg)); +} + +/** + * Perform an async atomic 16 bit add after the current tag + * switch completes. + * + * @param scraddr Scratch memory byte address to put response in. + *                Must be 8 byte aligned. + *                If a timeout occurs, the error bit (63) will be set. Otherwise + *                the value of the register before the update will be + *                returned + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 2 for 16 bit access. + * @param value   Signed value to add. + * @return Placed in the scratch pad register + */ +static inline void cvmx_hwfau_async_tagwait_fetch_and_add16(u64 scraddr, cvmx_fau_reg16_t reg, +							    s16 value) +{ +	cvmx_send_single(__cvmx_fau_iobdma_data(scraddr, value, 1, CVMX_FAU_OP_SIZE_16, reg)); +} + +/** + * Perform an async atomic 8 bit add after the current tag + * switch completes. + * + * @param scraddr Scratch memory byte address to put response in. + *                Must be 8 byte aligned. + *                If a timeout occurs, the error bit (63) will be set. Otherwise + *                the value of the register before the update will be + *                returned + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + * @param value   Signed value to add. + * @return Placed in the scratch pad register + */ +static inline void cvmx_hwfau_async_tagwait_fetch_and_add8(u64 scraddr, cvmx_fau_reg8_t reg, +							   int8_t value) +{ +	cvmx_send_single(__cvmx_fau_iobdma_data(scraddr, value, 1, CVMX_FAU_OP_SIZE_8, reg)); +} + +/** + * Perform an atomic 64 bit add + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 8 for 64 bit access. + * @param value   Signed value to add. + */ +static inline void cvmx_hwfau_atomic_add64(cvmx_fau_reg64_t reg, s64 value) +{ +	cvmx_write64_int64(__cvmx_hwfau_store_address(0, reg), value); +} + +/** + * Perform an atomic 32 bit add + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 4 for 32 bit access. + * @param value   Signed value to add. + */ +static inline void cvmx_hwfau_atomic_add32(cvmx_fau_reg32_t reg, s32 value) +{ +	reg ^= SWIZZLE_32; +	cvmx_write64_int32(__cvmx_hwfau_store_address(0, reg), value); +} + +/** + * Perform an atomic 16 bit add + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 2 for 16 bit access. + * @param value   Signed value to add. + */ +static inline void cvmx_hwfau_atomic_add16(cvmx_fau_reg16_t reg, s16 value) +{ +	reg ^= SWIZZLE_16; +	cvmx_write64_int16(__cvmx_hwfau_store_address(0, reg), value); +} + +/** + * Perform an atomic 8 bit add + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + * @param value   Signed value to add. + */ +static inline void cvmx_hwfau_atomic_add8(cvmx_fau_reg8_t reg, int8_t value) +{ +	reg ^= SWIZZLE_8; +	cvmx_write64_int8(__cvmx_hwfau_store_address(0, reg), value); +} + +/** + * Perform an atomic 64 bit write + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 8 for 64 bit access. + * @param value   Signed value to write. + */ +static inline void cvmx_hwfau_atomic_write64(cvmx_fau_reg64_t reg, s64 value) +{ +	cvmx_write64_int64(__cvmx_hwfau_store_address(1, reg), value); +} + +/** + * Perform an atomic 32 bit write + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 4 for 32 bit access. + * @param value   Signed value to write. + */ +static inline void cvmx_hwfau_atomic_write32(cvmx_fau_reg32_t reg, s32 value) +{ +	reg ^= SWIZZLE_32; +	cvmx_write64_int32(__cvmx_hwfau_store_address(1, reg), value); +} + +/** + * Perform an atomic 16 bit write + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + *                - Step by 2 for 16 bit access. + * @param value   Signed value to write. + */ +static inline void cvmx_hwfau_atomic_write16(cvmx_fau_reg16_t reg, s16 value) +{ +	reg ^= SWIZZLE_16; +	cvmx_write64_int16(__cvmx_hwfau_store_address(1, reg), value); +} + +/** + * Perform an atomic 8 bit write + * + * @param reg     FAU atomic register to access. 0 <= reg < 2048. + * @param value   Signed value to write. + */ +static inline void cvmx_hwfau_atomic_write8(cvmx_fau_reg8_t reg, int8_t value) +{ +	reg ^= SWIZZLE_8; +	cvmx_write64_int8(__cvmx_hwfau_store_address(1, reg), value); +} + +/** Allocates 64bit FAU register. + *  @return value is the base address of allocated FAU register + */ +int cvmx_fau64_alloc(int reserve); + +/** Allocates 32bit FAU register. + *  @return value is the base address of allocated FAU register + */ +int cvmx_fau32_alloc(int reserve); + +/** Allocates 16bit FAU register. + *  @return value is the base address of allocated FAU register + */ +int cvmx_fau16_alloc(int reserve); + +/** Allocates 8bit FAU register. + *  @return value is the base address of allocated FAU register + */ +int cvmx_fau8_alloc(int reserve); + +/** Frees the specified FAU register. + *  @param address Base address of register to release. + *  @return 0 on success; -1 on failure + */ +int cvmx_fau_free(int address); + +/** Display the fau registers array + */ +void cvmx_fau_show(void); + +#endif /* __CVMX_HWFAU_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-hwpko.h b/arch/mips/mach-octeon/include/mach/cvmx-hwpko.h new file mode 100644 index 00000000000..459c19bbc0f --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-hwpko.h @@ -0,0 +1,570 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Interface to the hardware Packet Output unit. + * + * Starting with SDK 1.7.0, the PKO output functions now support + * two types of locking. CVMX_PKO_LOCK_ATOMIC_TAG continues to + * function similarly to previous SDKs by using POW atomic tags + * to preserve ordering and exclusivity. As a new option, you + * can now pass CVMX_PKO_LOCK_CMD_QUEUE which uses a ll/sc + * memory based locking instead. This locking has the advantage + * of not affecting the tag state but doesn't preserve packet + * ordering. CVMX_PKO_LOCK_CMD_QUEUE is appropriate in most + * generic code while CVMX_PKO_LOCK_CMD_QUEUE should be used + * with hand tuned fast path code. + * + * Some of other SDK differences visible to the command command + * queuing: + * - PKO indexes are no longer stored in the FAU. A large + *   percentage of the FAU register block used to be tied up + *   maintaining PKO queue pointers. These are now stored in a + *   global named block. + * - The PKO <b>use_locking</b> parameter can now have a global + *   effect. Since all application use the same named block, + *   queue locking correctly applies across all operating + *   systems when using CVMX_PKO_LOCK_CMD_QUEUE. + * - PKO 3 word commands are now supported. Use + *   cvmx_pko_send_packet_finish3(). + */ + +#ifndef __CVMX_HWPKO_H__ +#define __CVMX_HWPKO_H__ + +#include "cvmx-hwfau.h" +#include "cvmx-fpa.h" +#include "cvmx-pow.h" +#include "cvmx-cmd-queue.h" +#include "cvmx-helper.h" +#include "cvmx-helper-util.h" +#include "cvmx-helper-cfg.h" + +/* Adjust the command buffer size by 1 word so that in the case of using only +** two word PKO commands no command words stradle buffers.  The useful values +** for this are 0 and 1. */ +#define CVMX_PKO_COMMAND_BUFFER_SIZE_ADJUST (1) + +#define CVMX_PKO_MAX_OUTPUT_QUEUES_STATIC 256 +#define CVMX_PKO_MAX_OUTPUT_QUEUES                                                                 \ +	((OCTEON_IS_OCTEON2() || OCTEON_IS_MODEL(OCTEON_CN70XX)) ? 256 : 128) +#define CVMX_PKO_NUM_OUTPUT_PORTS                                                                  \ +	((OCTEON_IS_MODEL(OCTEON_CN63XX)) ? 44 : (OCTEON_IS_MODEL(OCTEON_CN66XX) ? 48 : 40)) +#define CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID 63 +#define CVMX_PKO_QUEUE_STATIC_PRIORITY	    9 +#define CVMX_PKO_ILLEGAL_QUEUE		    0xFFFF +#define CVMX_PKO_MAX_QUEUE_DEPTH	    0 + +typedef enum { +	CVMX_PKO_SUCCESS, +	CVMX_PKO_INVALID_PORT, +	CVMX_PKO_INVALID_QUEUE, +	CVMX_PKO_INVALID_PRIORITY, +	CVMX_PKO_NO_MEMORY, +	CVMX_PKO_PORT_ALREADY_SETUP, +	CVMX_PKO_CMD_QUEUE_INIT_ERROR +} cvmx_pko_return_value_t; + +/** + * This enumeration represents the differnet locking modes supported by PKO. + */ +typedef enum { +	CVMX_PKO_LOCK_NONE = 0, +	CVMX_PKO_LOCK_ATOMIC_TAG = 1, +	CVMX_PKO_LOCK_CMD_QUEUE = 2, +} cvmx_pko_lock_t; + +typedef struct cvmx_pko_port_status { +	u32 packets; +	u64 octets; +	u64 doorbell; +} cvmx_pko_port_status_t; + +/** + * This structure defines the address to use on a packet enqueue + */ +typedef union { +	u64 u64; +	struct { +		cvmx_mips_space_t mem_space : 2; +		u64 reserved : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 reserved2 : 4; +		u64 reserved3 : 15; +		u64 port : 9; +		u64 queue : 9; +		u64 reserved4 : 3; +	} s; +} cvmx_pko_doorbell_address_t; + +/** + * Structure of the first packet output command word. + */ +typedef union { +	u64 u64; +	struct { +		cvmx_fau_op_size_t size1 : 2; +		cvmx_fau_op_size_t size0 : 2; +		u64 subone1 : 1; +		u64 reg1 : 11; +		u64 subone0 : 1; +		u64 reg0 : 11; +		u64 le : 1; +		u64 n2 : 1; +		u64 wqp : 1; +		u64 rsp : 1; +		u64 gather : 1; +		u64 ipoffp1 : 7; +		u64 ignore_i : 1; +		u64 dontfree : 1; +		u64 segs : 6; +		u64 total_bytes : 16; +	} s; +} cvmx_pko_command_word0_t; + +/** + * Call before any other calls to initialize the packet + * output system. + */ + +void cvmx_pko_hw_init(u8 pool, unsigned int bufsize); + +/** + * Enables the packet output hardware. It must already be + * configured. + */ +void cvmx_pko_enable(void); + +/** + * Disables the packet output. Does not affect any configuration. + */ +void cvmx_pko_disable(void); + +/** + * Shutdown and free resources required by packet output. + */ + +void cvmx_pko_shutdown(void); + +/** + * Configure a output port and the associated queues for use. + * + * @param port       Port to configure. + * @param base_queue First queue number to associate with this port. + * @param num_queues Number of queues t oassociate with this port + * @param priority   Array of priority levels for each queue. Values are + *                   allowed to be 1-8. A value of 8 get 8 times the traffic + *                   of a value of 1. There must be num_queues elements in the + *                   array. + */ +cvmx_pko_return_value_t cvmx_pko_config_port(int port, int base_queue, int num_queues, +					     const u8 priority[]); + +/** + * Ring the packet output doorbell. This tells the packet + * output hardware that "len" command words have been added + * to its pending list.  This command includes the required + * CVMX_SYNCWS before the doorbell ring. + * + * WARNING: This function may have to look up the proper PKO port in + * the IPD port to PKO port map, and is thus slower than calling + * cvmx_pko_doorbell_pkoid() directly if the PKO port identifier is + * known. + * + * @param ipd_port   The IPD port corresponding the to pko port the packet is for + * @param queue  Queue the packet is for + * @param len    Length of the command in 64 bit words + */ +static inline void cvmx_pko_doorbell(u64 ipd_port, u64 queue, u64 len) +{ +	cvmx_pko_doorbell_address_t ptr; +	u64 pko_port; + +	pko_port = ipd_port; +	if (octeon_has_feature(OCTEON_FEATURE_PKND)) +		pko_port = cvmx_helper_cfg_ipd2pko_port_base(ipd_port); + +	ptr.u64 = 0; +	ptr.s.mem_space = CVMX_IO_SEG; +	ptr.s.did = CVMX_OCT_DID_PKT_SEND; +	ptr.s.is_io = 1; +	ptr.s.port = pko_port; +	ptr.s.queue = queue; +	/* Need to make sure output queue data is in DRAM before doorbell write */ +	CVMX_SYNCWS; +	cvmx_write_io(ptr.u64, len); +} + +/** + * Prepare to send a packet.  This may initiate a tag switch to + * get exclusive access to the output queue structure, and + * performs other prep work for the packet send operation. + * + * cvmx_pko_send_packet_finish() MUST be called after this function is called, + * and must be called with the same port/queue/use_locking arguments. + * + * The use_locking parameter allows the caller to use three + * possible locking modes. + * - CVMX_PKO_LOCK_NONE + *      - PKO doesn't do any locking. It is the responsibility + *          of the application to make sure that no other core + *          is accessing the same queue at the same time. + * - CVMX_PKO_LOCK_ATOMIC_TAG + *      - PKO performs an atomic tagswitch to insure exclusive + *          access to the output queue. This will maintain + *          packet ordering on output. + * - CVMX_PKO_LOCK_CMD_QUEUE + *      - PKO uses the common command queue locks to insure + *          exclusive access to the output queue. This is a + *          memory based ll/sc. This is the most portable + *          locking mechanism. + * + * NOTE: If atomic locking is used, the POW entry CANNOT be + * descheduled, as it does not contain a valid WQE pointer. + * + * @param port   Port to send it on, this can be either IPD port or PKO + *		 port. + * @param queue  Queue to use + * @param use_locking + *               CVMX_PKO_LOCK_NONE, CVMX_PKO_LOCK_ATOMIC_TAG, or CVMX_PKO_LOCK_CMD_QUEUE + */ +static inline void cvmx_pko_send_packet_prepare(u64 port __attribute__((unused)), u64 queue, +						cvmx_pko_lock_t use_locking) +{ +	if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) { +		/* +		 * Must do a full switch here to handle all cases.  We use a +		 * fake WQE pointer, as the POW does not access this memory. +		 * The WQE pointer and group are only used if this work is +		 * descheduled, which is not supported by the +		 * cvmx_pko_send_packet_prepare/cvmx_pko_send_packet_finish +		 * combination. Note that this is a special case in which these +		 * fake values can be used - this is not a general technique. +		 */ +		u32 tag = CVMX_TAG_SW_BITS_INTERNAL << CVMX_TAG_SW_SHIFT | +			  CVMX_TAG_SUBGROUP_PKO << CVMX_TAG_SUBGROUP_SHIFT | +			  (CVMX_TAG_SUBGROUP_MASK & queue); +		cvmx_pow_tag_sw_full((cvmx_wqe_t *)cvmx_phys_to_ptr(0x80), tag, +				     CVMX_POW_TAG_TYPE_ATOMIC, 0); +	} +} + +#define cvmx_pko_send_packet_prepare_pkoid cvmx_pko_send_packet_prepare + +/** + * Complete packet output. cvmx_pko_send_packet_prepare() must be called exactly once before this, + * and the same parameters must be passed to both cvmx_pko_send_packet_prepare() and + * cvmx_pko_send_packet_finish(). + * + * WARNING: This function may have to look up the proper PKO port in + * the IPD port to PKO port map, and is thus slower than calling + * cvmx_pko_send_packet_finish_pkoid() directly if the PKO port + * identifier is known. + * + * @param ipd_port   The IPD port corresponding the to pko port the packet is for + * @param queue  Queue to use + * @param pko_command + *               PKO HW command word + * @param packet Packet to send + * @param use_locking + *               CVMX_PKO_LOCK_NONE, CVMX_PKO_LOCK_ATOMIC_TAG, or CVMX_PKO_LOCK_CMD_QUEUE + * + * @return returns CVMX_PKO_SUCCESS on success, or error code on failure of output + */ +static inline cvmx_pko_return_value_t +cvmx_hwpko_send_packet_finish(u64 ipd_port, u64 queue, cvmx_pko_command_word0_t pko_command, +			      cvmx_buf_ptr_t packet, cvmx_pko_lock_t use_locking) +{ +	cvmx_cmd_queue_result_t result; + +	if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) +		cvmx_pow_tag_sw_wait(); + +	result = cvmx_cmd_queue_write2(CVMX_CMD_QUEUE_PKO(queue), +				       (use_locking == CVMX_PKO_LOCK_CMD_QUEUE), pko_command.u64, +				       packet.u64); +	if (cvmx_likely(result == CVMX_CMD_QUEUE_SUCCESS)) { +		cvmx_pko_doorbell(ipd_port, queue, 2); +		return CVMX_PKO_SUCCESS; +	} else if ((result == CVMX_CMD_QUEUE_NO_MEMORY) || (result == CVMX_CMD_QUEUE_FULL)) { +		return CVMX_PKO_NO_MEMORY; +	} else { +		return CVMX_PKO_INVALID_QUEUE; +	} +} + +/** + * Complete packet output. cvmx_pko_send_packet_prepare() must be called exactly once before this, + * and the same parameters must be passed to both cvmx_pko_send_packet_prepare() and + * cvmx_pko_send_packet_finish(). + * + * WARNING: This function may have to look up the proper PKO port in + * the IPD port to PKO port map, and is thus slower than calling + * cvmx_pko_send_packet_finish3_pkoid() directly if the PKO port + * identifier is known. + * + * @param ipd_port   The IPD port corresponding the to pko port the packet is for + * @param queue  Queue to use + * @param pko_command + *               PKO HW command word + * @param packet Packet to send + * @param addr   Plysical address of a work queue entry or physical address to zero on complete. + * @param use_locking + *               CVMX_PKO_LOCK_NONE, CVMX_PKO_LOCK_ATOMIC_TAG, or CVMX_PKO_LOCK_CMD_QUEUE + * + * @return returns CVMX_PKO_SUCCESS on success, or error code on failure of output + */ +static inline cvmx_pko_return_value_t +cvmx_hwpko_send_packet_finish3(u64 ipd_port, u64 queue, cvmx_pko_command_word0_t pko_command, +			       cvmx_buf_ptr_t packet, u64 addr, cvmx_pko_lock_t use_locking) +{ +	cvmx_cmd_queue_result_t result; + +	if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) +		cvmx_pow_tag_sw_wait(); + +	result = cvmx_cmd_queue_write3(CVMX_CMD_QUEUE_PKO(queue), +				       (use_locking == CVMX_PKO_LOCK_CMD_QUEUE), pko_command.u64, +				       packet.u64, addr); +	if (cvmx_likely(result == CVMX_CMD_QUEUE_SUCCESS)) { +		cvmx_pko_doorbell(ipd_port, queue, 3); +		return CVMX_PKO_SUCCESS; +	} else if ((result == CVMX_CMD_QUEUE_NO_MEMORY) || (result == CVMX_CMD_QUEUE_FULL)) { +		return CVMX_PKO_NO_MEMORY; +	} else { +		return CVMX_PKO_INVALID_QUEUE; +	} +} + +/** + * Get the first pko_port for the (interface, index) + * + * @param interface + * @param index + */ +int cvmx_pko_get_base_pko_port(int interface, int index); + +/** + * Get the number of pko_ports for the (interface, index) + * + * @param interface + * @param index + */ +int cvmx_pko_get_num_pko_ports(int interface, int index); + +/** + * For a given port number, return the base pko output queue + * for the port. + * + * @param port   IPD port number + * @return Base output queue + */ +int cvmx_pko_get_base_queue(int port); + +/** + * For a given port number, return the number of pko output queues. + * + * @param port   IPD port number + * @return Number of output queues + */ +int cvmx_pko_get_num_queues(int port); + +/** + * Sets the internal FPA pool data structure for PKO comamnd queue. + * @param pool	fpa pool number yo use + * @param buffer_size	buffer size of pool + * @param buffer_count	number of buufers to allocate to pool + * + * @note the caller is responsable for setting up the pool with + * an appropriate buffer size and sufficient buffer count. + */ +void cvmx_pko_set_cmd_que_pool_config(s64 pool, u64 buffer_size, u64 buffer_count); + +/** + * Get the status counters for a port. + * + * @param ipd_port Port number (ipd_port) to get statistics for. + * @param clear    Set to 1 to clear the counters after they are read + * @param status   Where to put the results. + * + * Note: + *     - Only the doorbell for the base queue of the ipd_port is + *       collected. + *     - Retrieving the stats involves writing the index through + *       CVMX_PKO_REG_READ_IDX and reading the stat CSRs, in that + *       order. It is not MP-safe and caller should guarantee + *       atomicity. + */ +void cvmx_pko_get_port_status(u64 ipd_port, u64 clear, cvmx_pko_port_status_t *status); + +/** + * Rate limit a PKO port to a max packets/sec. This function is only + * supported on CN57XX, CN56XX, CN55XX, and CN54XX. + * + * @param port      Port to rate limit + * @param packets_s Maximum packet/sec + * @param burst     Maximum number of packets to burst in a row before rate + *                  limiting cuts in. + * + * @return Zero on success, negative on failure + */ +int cvmx_pko_rate_limit_packets(int port, int packets_s, int burst); + +/** + * Rate limit a PKO port to a max bits/sec. This function is only + * supported on CN57XX, CN56XX, CN55XX, and CN54XX. + * + * @param port   Port to rate limit + * @param bits_s PKO rate limit in bits/sec + * @param burst  Maximum number of bits to burst before rate + *               limiting cuts in. + * + * @return Zero on success, negative on failure + */ +int cvmx_pko_rate_limit_bits(int port, u64 bits_s, int burst); + +/** + * @INTERNAL + * + * Retrieve the PKO pipe number for a port + * + * @param interface + * @param index + * + * @return negative on error. + * + * This applies only to the non-loopback interfaces. + * + */ +int __cvmx_pko_get_pipe(int interface, int index); + +/** + * For a given PKO port number, return the base output queue + * for the port. + * + * @param pko_port   PKO port number + * @return           Base output queue + */ +int cvmx_pko_get_base_queue_pkoid(int pko_port); + +/** + * For a given PKO port number, return the number of output queues + * for the port. + * + * @param pko_port	PKO port number + * @return		the number of output queues + */ +int cvmx_pko_get_num_queues_pkoid(int pko_port); + +/** + * Ring the packet output doorbell. This tells the packet + * output hardware that "len" command words have been added + * to its pending list.  This command includes the required + * CVMX_SYNCWS before the doorbell ring. + * + * @param pko_port   Port the packet is for + * @param queue  Queue the packet is for + * @param len    Length of the command in 64 bit words + */ +static inline void cvmx_pko_doorbell_pkoid(u64 pko_port, u64 queue, u64 len) +{ +	cvmx_pko_doorbell_address_t ptr; + +	ptr.u64 = 0; +	ptr.s.mem_space = CVMX_IO_SEG; +	ptr.s.did = CVMX_OCT_DID_PKT_SEND; +	ptr.s.is_io = 1; +	ptr.s.port = pko_port; +	ptr.s.queue = queue; +	/* Need to make sure output queue data is in DRAM before doorbell write */ +	CVMX_SYNCWS; +	cvmx_write_io(ptr.u64, len); +} + +/** + * Complete packet output. cvmx_pko_send_packet_prepare() must be called exactly once before this, + * and the same parameters must be passed to both cvmx_pko_send_packet_prepare() and + * cvmx_pko_send_packet_finish_pkoid(). + * + * @param pko_port   Port to send it on + * @param queue  Queue to use + * @param pko_command + *               PKO HW command word + * @param packet Packet to send + * @param use_locking + *               CVMX_PKO_LOCK_NONE, CVMX_PKO_LOCK_ATOMIC_TAG, or CVMX_PKO_LOCK_CMD_QUEUE + * + * @return returns CVMX_PKO_SUCCESS on success, or error code on failure of output + */ +static inline cvmx_pko_return_value_t +cvmx_hwpko_send_packet_finish_pkoid(int pko_port, u64 queue, cvmx_pko_command_word0_t pko_command, +				    cvmx_buf_ptr_t packet, cvmx_pko_lock_t use_locking) +{ +	cvmx_cmd_queue_result_t result; + +	if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) +		cvmx_pow_tag_sw_wait(); + +	result = cvmx_cmd_queue_write2(CVMX_CMD_QUEUE_PKO(queue), +				       (use_locking == CVMX_PKO_LOCK_CMD_QUEUE), pko_command.u64, +				       packet.u64); +	if (cvmx_likely(result == CVMX_CMD_QUEUE_SUCCESS)) { +		cvmx_pko_doorbell_pkoid(pko_port, queue, 2); +		return CVMX_PKO_SUCCESS; +	} else if ((result == CVMX_CMD_QUEUE_NO_MEMORY) || (result == CVMX_CMD_QUEUE_FULL)) { +		return CVMX_PKO_NO_MEMORY; +	} else { +		return CVMX_PKO_INVALID_QUEUE; +	} +} + +/** + * Complete packet output. cvmx_pko_send_packet_prepare() must be called exactly once before this, + * and the same parameters must be passed to both cvmx_pko_send_packet_prepare() and + * cvmx_pko_send_packet_finish_pkoid(). + * + * @param pko_port   The PKO port the packet is for + * @param queue  Queue to use + * @param pko_command + *               PKO HW command word + * @param packet Packet to send + * @param addr   Plysical address of a work queue entry or physical address to zero on complete. + * @param use_locking + *               CVMX_PKO_LOCK_NONE, CVMX_PKO_LOCK_ATOMIC_TAG, or CVMX_PKO_LOCK_CMD_QUEUE + * + * @return returns CVMX_PKO_SUCCESS on success, or error code on failure of output + */ +static inline cvmx_pko_return_value_t +cvmx_hwpko_send_packet_finish3_pkoid(u64 pko_port, u64 queue, cvmx_pko_command_word0_t pko_command, +				     cvmx_buf_ptr_t packet, u64 addr, cvmx_pko_lock_t use_locking) +{ +	cvmx_cmd_queue_result_t result; + +	if (use_locking == CVMX_PKO_LOCK_ATOMIC_TAG) +		cvmx_pow_tag_sw_wait(); + +	result = cvmx_cmd_queue_write3(CVMX_CMD_QUEUE_PKO(queue), +				       (use_locking == CVMX_PKO_LOCK_CMD_QUEUE), pko_command.u64, +				       packet.u64, addr); +	if (cvmx_likely(result == CVMX_CMD_QUEUE_SUCCESS)) { +		cvmx_pko_doorbell_pkoid(pko_port, queue, 3); +		return CVMX_PKO_SUCCESS; +	} else if ((result == CVMX_CMD_QUEUE_NO_MEMORY) || (result == CVMX_CMD_QUEUE_FULL)) { +		return CVMX_PKO_NO_MEMORY; +	} else { +		return CVMX_PKO_INVALID_QUEUE; +	} +} + +/* + * Obtain the number of PKO commands pending in a queue + * + * @param queue is the queue identifier to be queried + * @return the number of commands pending transmission or -1 on error + */ +int cvmx_pko_queue_pend_count(cvmx_cmd_queue_id_t queue); + +void cvmx_pko_set_cmd_queue_pool_buffer_count(u64 buffer_count); + +#endif /* __CVMX_HWPKO_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-ilk.h b/arch/mips/mach-octeon/include/mach/cvmx-ilk.h new file mode 100644 index 00000000000..727298352c2 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-ilk.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * This file contains defines for the ILK interface + */ + +#ifndef __CVMX_ILK_H__ +#define __CVMX_ILK_H__ + +/* CSR typedefs have been moved to cvmx-ilk-defs.h */ + +/* + * Note: this macro must match the first ilk port in the ipd_port_map_68xx[] + * and ipd_port_map_78xx[] arrays. + */ +static inline int CVMX_ILK_GBL_BASE(void) +{ +	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) +		return 5; +	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) +		return 6; +	return -1; +} + +static inline int CVMX_ILK_QLM_BASE(void) +{ +	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) +		return 1; +	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) +		return 4; +	return -1; +} + +typedef struct { +	int intf_en : 1; +	int la_mode : 1; +	int reserved : 14; /* unused */ +	int lane_speed : 16; +	/* add more here */ +} cvmx_ilk_intf_t; + +#define CVMX_NUM_ILK_INTF 2 +static inline int CVMX_ILK_MAX_LANES(void) +{ +	if (OCTEON_IS_MODEL(OCTEON_CN68XX)) +		return 8; +	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) +		return 16; +	return -1; +} + +extern unsigned short cvmx_ilk_lane_mask[CVMX_MAX_NODES][CVMX_NUM_ILK_INTF]; + +typedef struct { +	unsigned int pipe; +	unsigned int chan; +} cvmx_ilk_pipe_chan_t; + +#define CVMX_ILK_MAX_PIPES 45 +/* Max number of channels allowed */ +#define CVMX_ILK_MAX_CHANS 256 + +extern int cvmx_ilk_chans[CVMX_MAX_NODES][CVMX_NUM_ILK_INTF]; + +typedef struct { +	unsigned int chan; +	unsigned int pknd; +} cvmx_ilk_chan_pknd_t; + +#define CVMX_ILK_MAX_PKNDS 16 /* must be <45 */ + +typedef struct { +	int *chan_list; /* for discrete channels. or, must be null */ +	unsigned int num_chans; + +	unsigned int chan_start; /* for continuous channels */ +	unsigned int chan_end; +	unsigned int chan_step; + +	unsigned int clr_on_rd; +} cvmx_ilk_stats_ctrl_t; + +#define CVMX_ILK_MAX_CAL      288 +#define CVMX_ILK_MAX_CAL_IDX  (CVMX_ILK_MAX_CAL / 8) +#define CVMX_ILK_TX_MIN_CAL   1 +#define CVMX_ILK_RX_MIN_CAL   1 +#define CVMX_ILK_CAL_GRP_SZ   8 +#define CVMX_ILK_PIPE_BPID_SZ 7 +#define CVMX_ILK_ENT_CTRL_SZ  2 +#define CVMX_ILK_RX_FIFO_WM   0x200 + +typedef enum { PIPE_BPID = 0, LINK, XOFF, XON } cvmx_ilk_cal_ent_ctrl_t; + +typedef struct { +	unsigned char pipe_bpid; +	cvmx_ilk_cal_ent_ctrl_t ent_ctrl; +} cvmx_ilk_cal_entry_t; + +typedef enum { CVMX_ILK_LPBK_DISA = 0, CVMX_ILK_LPBK_ENA } cvmx_ilk_lpbk_ena_t; + +typedef enum { CVMX_ILK_LPBK_INT = 0, CVMX_ILK_LPBK_EXT } cvmx_ilk_lpbk_mode_t; + +/** + * This header is placed in front of all received ILK look-aside mode packets + */ +typedef union { +	u64 u64; + +	struct { +		u32 reserved_63_57 : 7;	  /* bits 63...57 */ +		u32 nsp_cmd : 5;	  /* bits 56...52 */ +		u32 nsp_flags : 4;	  /* bits 51...48 */ +		u32 nsp_grp_id_upper : 6; /* bits 47...42 */ +		u32 reserved_41_40 : 2;	  /* bits 41...40 */ +		/* Protocol type, 1 for LA mode packet */ +		u32 la_mode : 1;	  /* bit  39      */ +		u32 nsp_grp_id_lower : 2; /* bits 38...37 */ +		u32 nsp_xid_upper : 4;	  /* bits 36...33 */ +		/* ILK channel number, 0 or 1 */ +		u32 ilk_channel : 1;   /* bit  32      */ +		u32 nsp_xid_lower : 8; /* bits 31...24 */ +		/* Unpredictable, may be any value */ +		u32 reserved_23_0 : 24; /* bits 23...0  */ +	} s; +} cvmx_ilk_la_nsp_compact_hdr_t; + +typedef struct cvmx_ilk_LA_mode_struct { +	int ilk_LA_mode; +	int ilk_LA_mode_cal_ena; +} cvmx_ilk_LA_mode_t; + +extern cvmx_ilk_LA_mode_t cvmx_ilk_LA_mode[CVMX_NUM_ILK_INTF]; + +int cvmx_ilk_use_la_mode(int interface, int channel); +int cvmx_ilk_start_interface(int interface, unsigned short num_lanes); +int cvmx_ilk_start_interface_la(int interface, unsigned char num_lanes); +int cvmx_ilk_set_pipe(int interface, int pipe_base, unsigned int pipe_len); +int cvmx_ilk_tx_set_channel(int interface, cvmx_ilk_pipe_chan_t *pch, unsigned int num_chs); +int cvmx_ilk_rx_set_pknd(int interface, cvmx_ilk_chan_pknd_t *chpknd, unsigned int num_pknd); +int cvmx_ilk_enable(int interface); +int cvmx_ilk_disable(int interface); +int cvmx_ilk_get_intf_ena(int interface); +int cvmx_ilk_get_chan_info(int interface, unsigned char **chans, unsigned char *num_chan); +cvmx_ilk_la_nsp_compact_hdr_t cvmx_ilk_enable_la_header(int ipd_port, int mode); +void cvmx_ilk_show_stats(int interface, cvmx_ilk_stats_ctrl_t *pstats); +int cvmx_ilk_cal_setup_rx(int interface, int cal_depth, cvmx_ilk_cal_entry_t *pent, int hi_wm, +			  unsigned char cal_ena); +int cvmx_ilk_cal_setup_tx(int interface, int cal_depth, cvmx_ilk_cal_entry_t *pent, +			  unsigned char cal_ena); +int cvmx_ilk_lpbk(int interface, cvmx_ilk_lpbk_ena_t enable, cvmx_ilk_lpbk_mode_t mode); +int cvmx_ilk_la_mode_enable_rx_calendar(int interface); + +#endif /* __CVMX_ILK_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-ipd.h b/arch/mips/mach-octeon/include/mach/cvmx-ipd.h new file mode 100644 index 00000000000..cdff36fffb5 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-ipd.h @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Interface to the hardware Input Packet Data unit. + */ + +#ifndef __CVMX_IPD_H__ +#define __CVMX_IPD_H__ + +#include "cvmx-pki.h" + +/* CSR typedefs have been moved to cvmx-ipd-defs.h */ + +typedef cvmx_ipd_1st_mbuff_skip_t cvmx_ipd_mbuff_not_first_skip_t; +typedef cvmx_ipd_1st_next_ptr_back_t cvmx_ipd_second_next_ptr_back_t; + +typedef struct cvmx_ipd_tag_fields { +	u64 ipv6_src_ip : 1; +	u64 ipv6_dst_ip : 1; +	u64 ipv6_src_port : 1; +	u64 ipv6_dst_port : 1; +	u64 ipv6_next_header : 1; +	u64 ipv4_src_ip : 1; +	u64 ipv4_dst_ip : 1; +	u64 ipv4_src_port : 1; +	u64 ipv4_dst_port : 1; +	u64 ipv4_protocol : 1; +	u64 input_port : 1; +} cvmx_ipd_tag_fields_t; + +typedef struct cvmx_pip_port_config { +	u64 parse_mode; +	u64 tag_type; +	u64 tag_mode; +	cvmx_ipd_tag_fields_t tag_fields; +} cvmx_pip_port_config_t; + +typedef struct cvmx_ipd_config_struct { +	u64 first_mbuf_skip; +	u64 not_first_mbuf_skip; +	u64 ipd_enable; +	u64 enable_len_M8_fix; +	u64 cache_mode; +	cvmx_fpa_pool_config_t packet_pool; +	cvmx_fpa_pool_config_t wqe_pool; +	cvmx_pip_port_config_t port_config; +} cvmx_ipd_config_t; + +extern cvmx_ipd_config_t cvmx_ipd_cfg; + +/** + * Gets the fpa pool number of packet pool + */ +static inline s64 cvmx_fpa_get_packet_pool(void) +{ +	return (cvmx_ipd_cfg.packet_pool.pool_num); +} + +/** + * Gets the buffer size of packet pool buffer + */ +static inline u64 cvmx_fpa_get_packet_pool_block_size(void) +{ +	return (cvmx_ipd_cfg.packet_pool.buffer_size); +} + +/** + * Gets the buffer count of packet pool + */ +static inline u64 cvmx_fpa_get_packet_pool_buffer_count(void) +{ +	return (cvmx_ipd_cfg.packet_pool.buffer_count); +} + +/** + * Gets the fpa pool number of wqe pool + */ +static inline s64 cvmx_fpa_get_wqe_pool(void) +{ +	return (cvmx_ipd_cfg.wqe_pool.pool_num); +} + +/** + * Gets the buffer size of wqe pool buffer + */ +static inline u64 cvmx_fpa_get_wqe_pool_block_size(void) +{ +	return (cvmx_ipd_cfg.wqe_pool.buffer_size); +} + +/** + * Gets the buffer count of wqe pool + */ +static inline u64 cvmx_fpa_get_wqe_pool_buffer_count(void) +{ +	return (cvmx_ipd_cfg.wqe_pool.buffer_count); +} + +/** + * Sets the ipd related configuration in internal structure which is then used + * for seting IPD hardware block + */ +int cvmx_ipd_set_config(cvmx_ipd_config_t ipd_config); + +/** + * Gets the ipd related configuration from internal structure. + */ +void cvmx_ipd_get_config(cvmx_ipd_config_t *ipd_config); + +/** + * Sets the internal FPA pool data structure for packet buffer pool. + * @param pool	fpa pool number yo use + * @param buffer_size	buffer size of pool + * @param buffer_count	number of buufers to allocate to pool + */ +void cvmx_ipd_set_packet_pool_config(s64 pool, u64 buffer_size, u64 buffer_count); + +/** + * Sets the internal FPA pool data structure for wqe pool. + * @param pool	fpa pool number yo use + * @param buffer_size	buffer size of pool + * @param buffer_count	number of buufers to allocate to pool + */ +void cvmx_ipd_set_wqe_pool_config(s64 pool, u64 buffer_size, u64 buffer_count); + +/** + * Gets the FPA packet buffer pool parameters. + */ +static inline void cvmx_fpa_get_packet_pool_config(s64 *pool, u64 *buffer_size, u64 *buffer_count) +{ +	if (pool) +		*pool = cvmx_ipd_cfg.packet_pool.pool_num; +	if (buffer_size) +		*buffer_size = cvmx_ipd_cfg.packet_pool.buffer_size; +	if (buffer_count) +		*buffer_count = cvmx_ipd_cfg.packet_pool.buffer_count; +} + +/** + * Sets the FPA packet buffer pool parameters. + */ +static inline void cvmx_fpa_set_packet_pool_config(s64 pool, u64 buffer_size, u64 buffer_count) +{ +	cvmx_ipd_set_packet_pool_config(pool, buffer_size, buffer_count); +} + +/** + * Gets the FPA WQE pool parameters. + */ +static inline void cvmx_fpa_get_wqe_pool_config(s64 *pool, u64 *buffer_size, u64 *buffer_count) +{ +	if (pool) +		*pool = cvmx_ipd_cfg.wqe_pool.pool_num; +	if (buffer_size) +		*buffer_size = cvmx_ipd_cfg.wqe_pool.buffer_size; +	if (buffer_count) +		*buffer_count = cvmx_ipd_cfg.wqe_pool.buffer_count; +} + +/** + * Sets the FPA WQE pool parameters. + */ +static inline void cvmx_fpa_set_wqe_pool_config(s64 pool, u64 buffer_size, u64 buffer_count) +{ +	cvmx_ipd_set_wqe_pool_config(pool, buffer_size, buffer_count); +} + +/** + * Configure IPD + * + * @param mbuff_size Packets buffer size in 8 byte words + * @param first_mbuff_skip + *                   Number of 8 byte words to skip in the first buffer + * @param not_first_mbuff_skip + *                   Number of 8 byte words to skip in each following buffer + * @param first_back Must be same as first_mbuff_skip / 128 + * @param second_back + *                   Must be same as not_first_mbuff_skip / 128 + * @param wqe_fpa_pool + *                   FPA pool to get work entries from + * @param cache_mode + * @param back_pres_enable_flag + *                   Enable or disable port back pressure at a global level. + *                   This should always be 1 as more accurate control can be + *                   found in IPD_PORTX_BP_PAGE_CNT[BP_ENB]. + */ +void cvmx_ipd_config(u64 mbuff_size, u64 first_mbuff_skip, u64 not_first_mbuff_skip, u64 first_back, +		     u64 second_back, u64 wqe_fpa_pool, cvmx_ipd_mode_t cache_mode, +		     u64 back_pres_enable_flag); +/** + * Enable IPD + */ +void cvmx_ipd_enable(void); + +/** + * Disable IPD + */ +void cvmx_ipd_disable(void); + +void __cvmx_ipd_free_ptr(void); + +void cvmx_ipd_set_packet_pool_buffer_count(u64 buffer_count); +void cvmx_ipd_set_wqe_pool_buffer_count(u64 buffer_count); + +/** + * Setup Random Early Drop on a specific input queue + * + * @param queue  Input queue to setup RED on (0-7) + * @param pass_thresh + *               Packets will begin slowly dropping when there are less than + *               this many packet buffers free in FPA 0. + * @param drop_thresh + *               All incoming packets will be dropped when there are less + *               than this many free packet buffers in FPA 0. + * @return Zero on success. Negative on failure + */ +int cvmx_ipd_setup_red_queue(int queue, int pass_thresh, int drop_thresh); + +/** + * Setup Random Early Drop to automatically begin dropping packets. + * + * @param pass_thresh + *               Packets will begin slowly dropping when there are less than + *               this many packet buffers free in FPA 0. + * @param drop_thresh + *               All incoming packets will be dropped when there are less + *               than this many free packet buffers in FPA 0. + * @return Zero on success. Negative on failure + */ +int cvmx_ipd_setup_red(int pass_thresh, int drop_thresh); + +#endif /*  __CVMX_IPD_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-packet.h b/arch/mips/mach-octeon/include/mach/cvmx-packet.h new file mode 100644 index 00000000000..f3cfe9c64f4 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-packet.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Packet buffer defines. + */ + +#ifndef __CVMX_PACKET_H__ +#define __CVMX_PACKET_H__ + +union cvmx_buf_ptr_pki { +	u64 u64; +	struct { +		u64 size : 16; +		u64 packet_outside_wqe : 1; +		u64 rsvd0 : 5; +		u64 addr : 42; +	}; +}; + +typedef union cvmx_buf_ptr_pki cvmx_buf_ptr_pki_t; + +/** + * This structure defines a buffer pointer on Octeon + */ +union cvmx_buf_ptr { +	void *ptr; +	u64 u64; +	struct { +		u64 i : 1; +		u64 back : 4; +		u64 pool : 3; +		u64 size : 16; +		u64 addr : 40; +	} s; +}; + +typedef union cvmx_buf_ptr cvmx_buf_ptr_t; + +#endif /*  __CVMX_PACKET_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pcie.h b/arch/mips/mach-octeon/include/mach/cvmx-pcie.h new file mode 100644 index 00000000000..a819196c021 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pcie.h @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + */ + +#ifndef __CVMX_PCIE_H__ +#define __CVMX_PCIE_H__ + +#define CVMX_PCIE_MAX_PORTS 4 +#define CVMX_PCIE_PORTS                                                                            \ +	((OCTEON_IS_MODEL(OCTEON_CN78XX) || OCTEON_IS_MODEL(OCTEON_CN73XX)) ?                      \ +		       CVMX_PCIE_MAX_PORTS :                                                             \ +		       (OCTEON_IS_MODEL(OCTEON_CN70XX) ? 3 : 2)) + +/* + * The physical memory base mapped by BAR1.  256MB at the end of the + * first 4GB. + */ +#define CVMX_PCIE_BAR1_PHYS_BASE ((1ull << 32) - (1ull << 28)) +#define CVMX_PCIE_BAR1_PHYS_SIZE BIT_ULL(28) + +/* + * The RC base of BAR1.  gen1 has a 39-bit BAR2, gen2 has 41-bit BAR2, + * place BAR1 so it is the same for both. + */ +#define CVMX_PCIE_BAR1_RC_BASE BIT_ULL(41) + +typedef union { +	u64 u64; +	struct { +		u64 upper : 2;		 /* Normally 2 for XKPHYS */ +		u64 reserved_49_61 : 13; /* Must be zero */ +		u64 io : 1;		 /* 1 for IO space access */ +		u64 did : 5;		 /* PCIe DID = 3 */ +		u64 subdid : 3;		 /* PCIe SubDID = 1 */ +		u64 reserved_38_39 : 2;	 /* Must be zero */ +		u64 node : 2;		 /* Numa node number */ +		u64 es : 2;		 /* Endian swap = 1 */ +		u64 port : 2;		 /* PCIe port 0,1 */ +		u64 reserved_29_31 : 3;	 /* Must be zero */ +		u64 ty : 1; +		u64 bus : 8; +		u64 dev : 5; +		u64 func : 3; +		u64 reg : 12; +	} config; +	struct { +		u64 upper : 2;		 /* Normally 2 for XKPHYS */ +		u64 reserved_49_61 : 13; /* Must be zero */ +		u64 io : 1;		 /* 1 for IO space access */ +		u64 did : 5;		 /* PCIe DID = 3 */ +		u64 subdid : 3;		 /* PCIe SubDID = 2 */ +		u64 reserved_38_39 : 2;	 /* Must be zero */ +		u64 node : 2;		 /* Numa node number */ +		u64 es : 2;		 /* Endian swap = 1 */ +		u64 port : 2;		 /* PCIe port 0,1 */ +		u64 address : 32;	 /* PCIe IO address */ +	} io; +	struct { +		u64 upper : 2;		 /* Normally 2 for XKPHYS */ +		u64 reserved_49_61 : 13; /* Must be zero */ +		u64 io : 1;		 /* 1 for IO space access */ +		u64 did : 5;		 /* PCIe DID = 3 */ +		u64 subdid : 3;		 /* PCIe SubDID = 3-6 */ +		u64 reserved_38_39 : 2;	 /* Must be zero */ +		u64 node : 2;		 /* Numa node number */ +		u64 address : 36;	 /* PCIe Mem address */ +	} mem; +} cvmx_pcie_address_t; + +/** + * Return the Core virtual base address for PCIe IO access. IOs are + * read/written as an offset from this address. + * + * @param pcie_port PCIe port the IO is for + * + * @return 64bit Octeon IO base address for read/write + */ +u64 cvmx_pcie_get_io_base_address(int pcie_port); + +/** + * Size of the IO address region returned at address + * cvmx_pcie_get_io_base_address() + * + * @param pcie_port PCIe port the IO is for + * + * @return Size of the IO window + */ +u64 cvmx_pcie_get_io_size(int pcie_port); + +/** + * Return the Core virtual base address for PCIe MEM access. Memory is + * read/written as an offset from this address. + * + * @param pcie_port PCIe port the IO is for + * + * @return 64bit Octeon IO base address for read/write + */ +u64 cvmx_pcie_get_mem_base_address(int pcie_port); + +/** + * Size of the Mem address region returned at address + * cvmx_pcie_get_mem_base_address() + * + * @param pcie_port PCIe port the IO is for + * + * @return Size of the Mem window + */ +u64 cvmx_pcie_get_mem_size(int pcie_port); + +/** + * Initialize a PCIe port for use in host(RC) mode. It doesn't enumerate the bus. + * + * @param pcie_port PCIe port to initialize + * + * @return Zero on success + */ +int cvmx_pcie_rc_initialize(int pcie_port); + +/** + * Shutdown a PCIe port and put it in reset + * + * @param pcie_port PCIe port to shutdown + * + * @return Zero on success + */ +int cvmx_pcie_rc_shutdown(int pcie_port); + +/** + * Read 8bits from a Device's config space + * + * @param pcie_port PCIe port the device is on + * @param bus       Sub bus + * @param dev       Device ID + * @param fn        Device sub function + * @param reg       Register to access + * + * @return Result of the read + */ +u8 cvmx_pcie_config_read8(int pcie_port, int bus, int dev, int fn, int reg); + +/** + * Read 16bits from a Device's config space + * + * @param pcie_port PCIe port the device is on + * @param bus       Sub bus + * @param dev       Device ID + * @param fn        Device sub function + * @param reg       Register to access + * + * @return Result of the read + */ +u16 cvmx_pcie_config_read16(int pcie_port, int bus, int dev, int fn, int reg); + +/** + * Read 32bits from a Device's config space + * + * @param pcie_port PCIe port the device is on + * @param bus       Sub bus + * @param dev       Device ID + * @param fn        Device sub function + * @param reg       Register to access + * + * @return Result of the read + */ +u32 cvmx_pcie_config_read32(int pcie_port, int bus, int dev, int fn, int reg); + +/** + * Write 8bits to a Device's config space + * + * @param pcie_port PCIe port the device is on + * @param bus       Sub bus + * @param dev       Device ID + * @param fn        Device sub function + * @param reg       Register to access + * @param val       Value to write + */ +void cvmx_pcie_config_write8(int pcie_port, int bus, int dev, int fn, int reg, u8 val); + +/** + * Write 16bits to a Device's config space + * + * @param pcie_port PCIe port the device is on + * @param bus       Sub bus + * @param dev       Device ID + * @param fn        Device sub function + * @param reg       Register to access + * @param val       Value to write + */ +void cvmx_pcie_config_write16(int pcie_port, int bus, int dev, int fn, int reg, u16 val); + +/** + * Write 32bits to a Device's config space + * + * @param pcie_port PCIe port the device is on + * @param bus       Sub bus + * @param dev       Device ID + * @param fn        Device sub function + * @param reg       Register to access + * @param val       Value to write + */ +void cvmx_pcie_config_write32(int pcie_port, int bus, int dev, int fn, int reg, u32 val); + +/** + * Read a PCIe config space register indirectly. This is used for + * registers of the form PCIEEP_CFG??? and PCIERC?_CFG???. + * + * @param pcie_port  PCIe port to read from + * @param cfg_offset Address to read + * + * @return Value read + */ +u32 cvmx_pcie_cfgx_read(int pcie_port, u32 cfg_offset); +u32 cvmx_pcie_cfgx_read_node(int node, int pcie_port, u32 cfg_offset); + +/** + * Write a PCIe config space register indirectly. This is used for + * registers of the form PCIEEP_CFG??? and PCIERC?_CFG???. + * + * @param pcie_port  PCIe port to write to + * @param cfg_offset Address to write + * @param val        Value to write + */ +void cvmx_pcie_cfgx_write(int pcie_port, u32 cfg_offset, u32 val); +void cvmx_pcie_cfgx_write_node(int node, int pcie_port, u32 cfg_offset, u32 val); + +/** + * Write a 32bit value to the Octeon NPEI register space + * + * @param address Address to write to + * @param val     Value to write + */ +static inline void cvmx_pcie_npei_write32(u64 address, u32 val) +{ +	cvmx_write64_uint32(address ^ 4, val); +	cvmx_read64_uint32(address ^ 4); +} + +/** + * Read a 32bit value from the Octeon NPEI register space + * + * @param address Address to read + * @return The result + */ +static inline u32 cvmx_pcie_npei_read32(u64 address) +{ +	return cvmx_read64_uint32(address ^ 4); +} + +/** + * Initialize a PCIe port for use in target(EP) mode. + * + * @param pcie_port PCIe port to initialize + * + * @return Zero on success + */ +int cvmx_pcie_ep_initialize(int pcie_port); + +/** + * Wait for posted PCIe read/writes to reach the other side of + * the internal PCIe switch. This will insure that core + * read/writes are posted before anything after this function + * is called. This may be necessary when writing to memory that + * will later be read using the DMA/PKT engines. + * + * @param pcie_port PCIe port to wait for + */ +void cvmx_pcie_wait_for_pending(int pcie_port); + +/** + * Returns if a PCIe port is in host or target mode. + * + * @param pcie_port PCIe port number (PEM number) + * + * @return 0 if PCIe port is in target mode, !0 if in host mode. + */ +int cvmx_pcie_is_host_mode(int pcie_port); + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pip.h b/arch/mips/mach-octeon/include/mach/cvmx-pip.h new file mode 100644 index 00000000000..013f533fb7b --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pip.h @@ -0,0 +1,1080 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Interface to the hardware Packet Input Processing unit. + */ + +#ifndef __CVMX_PIP_H__ +#define __CVMX_PIP_H__ + +#include "cvmx-wqe.h" +#include "cvmx-pki.h" +#include "cvmx-helper-pki.h" + +#include "cvmx-helper.h" +#include "cvmx-helper-util.h" +#include "cvmx-pki-resources.h" + +#define CVMX_PIP_NUM_INPUT_PORTS 46 +#define CVMX_PIP_NUM_WATCHERS	 8 + +/* + * Encodes the different error and exception codes + */ +typedef enum { +	CVMX_PIP_L4_NO_ERR = 0ull, +	/*        1  = TCP (UDP) packet not long enough to cover TCP (UDP) header */ +	CVMX_PIP_L4_MAL_ERR = 1ull, +	/*        2  = TCP/UDP checksum failure */ +	CVMX_PIP_CHK_ERR = 2ull, +	/*        3  = TCP/UDP length check (TCP/UDP length does not match IP length) */ +	CVMX_PIP_L4_LENGTH_ERR = 3ull, +	/*        4  = illegal TCP/UDP port (either source or dest port is zero) */ +	CVMX_PIP_BAD_PRT_ERR = 4ull, +	/*        8  = TCP flags = FIN only */ +	CVMX_PIP_TCP_FLG8_ERR = 8ull, +	/*        9  = TCP flags = 0 */ +	CVMX_PIP_TCP_FLG9_ERR = 9ull, +	/*        10 = TCP flags = FIN+RST+* */ +	CVMX_PIP_TCP_FLG10_ERR = 10ull, +	/*        11 = TCP flags = SYN+URG+* */ +	CVMX_PIP_TCP_FLG11_ERR = 11ull, +	/*        12 = TCP flags = SYN+RST+* */ +	CVMX_PIP_TCP_FLG12_ERR = 12ull, +	/*        13 = TCP flags = SYN+FIN+* */ +	CVMX_PIP_TCP_FLG13_ERR = 13ull +} cvmx_pip_l4_err_t; + +typedef enum { +	CVMX_PIP_IP_NO_ERR = 0ull, +	/*        1 = not IPv4 or IPv6 */ +	CVMX_PIP_NOT_IP = 1ull, +	/*        2 = IPv4 header checksum violation */ +	CVMX_PIP_IPV4_HDR_CHK = 2ull, +	/*        3 = malformed (packet not long enough to cover IP hdr) */ +	CVMX_PIP_IP_MAL_HDR = 3ull, +	/*        4 = malformed (packet not long enough to cover len in IP hdr) */ +	CVMX_PIP_IP_MAL_PKT = 4ull, +	/*        5 = TTL / hop count equal zero */ +	CVMX_PIP_TTL_HOP = 5ull, +	/*        6 = IPv4 options / IPv6 early extension headers */ +	CVMX_PIP_OPTS = 6ull +} cvmx_pip_ip_exc_t; + +/** + * NOTES + *       late collision (data received before collision) + *            late collisions cannot be detected by the receiver + *            they would appear as JAM bits which would appear as bad FCS + *            or carrier extend error which is CVMX_PIP_EXTEND_ERR + */ +typedef enum { +	/** +	 * No error +	 */ +	CVMX_PIP_RX_NO_ERR = 0ull, + +	CVMX_PIP_PARTIAL_ERR = +		1ull, /* RGM+SPI            1 = partially received packet (buffering/bandwidth not adequate) */ +	CVMX_PIP_JABBER_ERR = +		2ull, /* RGM+SPI            2 = receive packet too large and truncated */ +	CVMX_PIP_OVER_FCS_ERR = +		3ull, /* RGM                3 = max frame error (pkt len > max frame len) (with FCS error) */ +	CVMX_PIP_OVER_ERR = +		4ull, /* RGM+SPI            4 = max frame error (pkt len > max frame len) */ +	CVMX_PIP_ALIGN_ERR = +		5ull, /* RGM                5 = nibble error (data not byte multiple - 100M and 10M only) */ +	CVMX_PIP_UNDER_FCS_ERR = +		6ull, /* RGM                6 = min frame error (pkt len < min frame len) (with FCS error) */ +	CVMX_PIP_GMX_FCS_ERR = 7ull, /* RGM                7 = FCS error */ +	CVMX_PIP_UNDER_ERR = +		8ull, /* RGM+SPI            8 = min frame error (pkt len < min frame len) */ +	CVMX_PIP_EXTEND_ERR = 9ull, /* RGM                9 = Frame carrier extend error */ +	CVMX_PIP_TERMINATE_ERR = +		9ull, /* XAUI               9 = Packet was terminated with an idle cycle */ +	CVMX_PIP_LENGTH_ERR = +		10ull, /* RGM               10 = length mismatch (len did not match len in L2 length/type) */ +	CVMX_PIP_DAT_ERR = +		11ull, /* RGM               11 = Frame error (some or all data bits marked err) */ +	CVMX_PIP_DIP_ERR = 11ull, /*     SPI           11 = DIP4 error */ +	CVMX_PIP_SKIP_ERR = +		12ull, /* RGM               12 = packet was not large enough to pass the skipper - no inspection could occur */ +	CVMX_PIP_NIBBLE_ERR = +		13ull, /* RGM               13 = studder error (data not repeated - 100M and 10M only) */ +	CVMX_PIP_PIP_FCS = 16L, /* RGM+SPI           16 = FCS error */ +	CVMX_PIP_PIP_SKIP_ERR = +		17L, /* RGM+SPI+PCI       17 = packet was not large enough to pass the skipper - no inspection could occur */ +	CVMX_PIP_PIP_L2_MAL_HDR = +		18L, /* RGM+SPI+PCI       18 = malformed l2 (packet not long enough to cover L2 hdr) */ +	CVMX_PIP_PUNY_ERR = +		47L /* SGMII             47 = PUNY error (packet was 4B or less when FCS stripping is enabled) */ +	/* NOTES +	 *       xx = late collision (data received before collision) +	 *            late collisions cannot be detected by the receiver +	 *            they would appear as JAM bits which would appear as bad FCS +	 *            or carrier extend error which is CVMX_PIP_EXTEND_ERR +	 */ +} cvmx_pip_rcv_err_t; + +/** + * This defines the err_code field errors in the work Q entry + */ +typedef union { +	cvmx_pip_l4_err_t l4_err; +	cvmx_pip_ip_exc_t ip_exc; +	cvmx_pip_rcv_err_t rcv_err; +} cvmx_pip_err_t; + +/** + * Status statistics for a port + */ +typedef struct { +	u64 dropped_octets; +	u64 dropped_packets; +	u64 pci_raw_packets; +	u64 octets; +	u64 packets; +	u64 multicast_packets; +	u64 broadcast_packets; +	u64 len_64_packets; +	u64 len_65_127_packets; +	u64 len_128_255_packets; +	u64 len_256_511_packets; +	u64 len_512_1023_packets; +	u64 len_1024_1518_packets; +	u64 len_1519_max_packets; +	u64 fcs_align_err_packets; +	u64 runt_packets; +	u64 runt_crc_packets; +	u64 oversize_packets; +	u64 oversize_crc_packets; +	u64 inb_packets; +	u64 inb_octets; +	u64 inb_errors; +	u64 mcast_l2_red_packets; +	u64 bcast_l2_red_packets; +	u64 mcast_l3_red_packets; +	u64 bcast_l3_red_packets; +} cvmx_pip_port_status_t; + +/** + * Definition of the PIP custom header that can be prepended + * to a packet by external hardware. + */ +typedef union { +	u64 u64; +	struct { +		u64 rawfull : 1; +		u64 reserved0 : 5; +		cvmx_pip_port_parse_mode_t parse_mode : 2; +		u64 reserved1 : 1; +		u64 skip_len : 7; +		u64 grpext : 2; +		u64 nqos : 1; +		u64 ngrp : 1; +		u64 ntt : 1; +		u64 ntag : 1; +		u64 qos : 3; +		u64 grp : 4; +		u64 rs : 1; +		cvmx_pow_tag_type_t tag_type : 2; +		u64 tag : 32; +	} s; +} cvmx_pip_pkt_inst_hdr_t; + +enum cvmx_pki_pcam_match { +	CVMX_PKI_PCAM_MATCH_IP, +	CVMX_PKI_PCAM_MATCH_IPV4, +	CVMX_PKI_PCAM_MATCH_IPV6, +	CVMX_PKI_PCAM_MATCH_TCP +}; + +/* CSR typedefs have been moved to cvmx-pip-defs.h */ +static inline int cvmx_pip_config_watcher(int index, int type, u16 match, u16 mask, int grp, +					  int qos) +{ +	if (index >= CVMX_PIP_NUM_WATCHERS) { +		debug("ERROR: pip watcher %d is > than supported\n", index); +		return -1; +	} +	if (octeon_has_feature(OCTEON_FEATURE_PKI)) { +		/* store in software for now, only when the watcher is enabled program the entry*/ +		if (type == CVMX_PIP_QOS_WATCH_PROTNH) { +			qos_watcher[index].field = CVMX_PKI_PCAM_TERM_L3_FLAGS; +			qos_watcher[index].data = (u32)(match << 16); +			qos_watcher[index].data_mask = (u32)(mask << 16); +			qos_watcher[index].advance = 0; +		} else if (type == CVMX_PIP_QOS_WATCH_TCP) { +			qos_watcher[index].field = CVMX_PKI_PCAM_TERM_L4_PORT; +			qos_watcher[index].data = 0x060000; +			qos_watcher[index].data |= (u32)match; +			qos_watcher[index].data_mask = (u32)(mask); +			qos_watcher[index].advance = 0; +		} else if (type == CVMX_PIP_QOS_WATCH_UDP) { +			qos_watcher[index].field = CVMX_PKI_PCAM_TERM_L4_PORT; +			qos_watcher[index].data = 0x110000; +			qos_watcher[index].data |= (u32)match; +			qos_watcher[index].data_mask = (u32)(mask); +			qos_watcher[index].advance = 0; +		} else if (type == 0x4 /*CVMX_PIP_QOS_WATCH_ETHERTYPE*/) { +			qos_watcher[index].field = CVMX_PKI_PCAM_TERM_ETHTYPE0; +			if (match == 0x8100) { +				debug("ERROR: default vlan entry already exist, cant set watcher\n"); +				return -1; +			} +			qos_watcher[index].data = (u32)(match << 16); +			qos_watcher[index].data_mask = (u32)(mask << 16); +			qos_watcher[index].advance = 4; +		} else { +			debug("ERROR: Unsupported watcher type %d\n", type); +			return -1; +		} +		if (grp >= 32) { +			debug("ERROR: grp %d out of range for backward compat 78xx\n", grp); +			return -1; +		} +		qos_watcher[index].sso_grp = (u8)(grp << 3 | qos); +		qos_watcher[index].configured = 1; +	} else { +		/* Implement it later */ +	} +	return 0; +} + +static inline int __cvmx_pip_set_tag_type(int node, int style, int tag_type, int field) +{ +	struct cvmx_pki_style_config style_cfg; +	int style_num; +	int pcam_offset; +	int bank; +	struct cvmx_pki_pcam_input pcam_input; +	struct cvmx_pki_pcam_action pcam_action; + +	/* All other style parameters remain same except tag type */ +	cvmx_pki_read_style_config(node, style, CVMX_PKI_CLUSTER_ALL, &style_cfg); +	style_cfg.parm_cfg.tag_type = (enum cvmx_sso_tag_type)tag_type; +	style_num = cvmx_pki_style_alloc(node, -1); +	if (style_num < 0) { +		debug("ERROR: style not available to set tag type\n"); +		return -1; +	} +	cvmx_pki_write_style_config(node, style_num, CVMX_PKI_CLUSTER_ALL, &style_cfg); +	memset(&pcam_input, 0, sizeof(pcam_input)); +	memset(&pcam_action, 0, sizeof(pcam_action)); +	pcam_input.style = style; +	pcam_input.style_mask = 0xff; +	if (field == CVMX_PKI_PCAM_MATCH_IP) { +		pcam_input.field = CVMX_PKI_PCAM_TERM_ETHTYPE0; +		pcam_input.field_mask = 0xff; +		pcam_input.data = 0x08000000; +		pcam_input.data_mask = 0xffff0000; +		pcam_action.pointer_advance = 4; +		/* legacy will write to all clusters*/ +		bank = 0; +		pcam_offset = cvmx_pki_pcam_entry_alloc(node, CVMX_PKI_FIND_AVAL_ENTRY, bank, +							CVMX_PKI_CLUSTER_ALL); +		if (pcam_offset < 0) { +			debug("ERROR: pcam entry not available to enable qos watcher\n"); +			cvmx_pki_style_free(node, style_num); +			return -1; +		} +		pcam_action.parse_mode_chg = CVMX_PKI_PARSE_NO_CHG; +		pcam_action.layer_type_set = CVMX_PKI_LTYPE_E_NONE; +		pcam_action.style_add = (u8)(style_num - style); +		cvmx_pki_pcam_write_entry(node, pcam_offset, CVMX_PKI_CLUSTER_ALL, pcam_input, +					  pcam_action); +		field = CVMX_PKI_PCAM_MATCH_IPV6; +	} +	if (field == CVMX_PKI_PCAM_MATCH_IPV4) { +		pcam_input.field = CVMX_PKI_PCAM_TERM_ETHTYPE0; +		pcam_input.field_mask = 0xff; +		pcam_input.data = 0x08000000; +		pcam_input.data_mask = 0xffff0000; +		pcam_action.pointer_advance = 4; +	} else if (field == CVMX_PKI_PCAM_MATCH_IPV6) { +		pcam_input.field = CVMX_PKI_PCAM_TERM_ETHTYPE0; +		pcam_input.field_mask = 0xff; +		pcam_input.data = 0x86dd00000; +		pcam_input.data_mask = 0xffff0000; +		pcam_action.pointer_advance = 4; +	} else if (field == CVMX_PKI_PCAM_MATCH_TCP) { +		pcam_input.field = CVMX_PKI_PCAM_TERM_L4_PORT; +		pcam_input.field_mask = 0xff; +		pcam_input.data = 0x60000; +		pcam_input.data_mask = 0xff0000; +		pcam_action.pointer_advance = 0; +	} +	pcam_action.parse_mode_chg = CVMX_PKI_PARSE_NO_CHG; +	pcam_action.layer_type_set = CVMX_PKI_LTYPE_E_NONE; +	pcam_action.style_add = (u8)(style_num - style); +	bank = pcam_input.field & 0x01; +	pcam_offset = cvmx_pki_pcam_entry_alloc(node, CVMX_PKI_FIND_AVAL_ENTRY, bank, +						CVMX_PKI_CLUSTER_ALL); +	if (pcam_offset < 0) { +		debug("ERROR: pcam entry not available to enable qos watcher\n"); +		cvmx_pki_style_free(node, style_num); +		return -1; +	} +	cvmx_pki_pcam_write_entry(node, pcam_offset, CVMX_PKI_CLUSTER_ALL, pcam_input, pcam_action); +	return style_num; +} + +/* Only for legacy internal use */ +static inline int __cvmx_pip_enable_watcher_78xx(int node, int index, int style) +{ +	struct cvmx_pki_style_config style_cfg; +	struct cvmx_pki_qpg_config qpg_cfg; +	struct cvmx_pki_pcam_input pcam_input; +	struct cvmx_pki_pcam_action pcam_action; +	int style_num; +	int qpg_offset; +	int pcam_offset; +	int bank; + +	if (!qos_watcher[index].configured) { +		debug("ERROR: qos watcher %d should be configured before enable\n", index); +		return -1; +	} +	/* All other style parameters remain same except grp and qos and qps base */ +	cvmx_pki_read_style_config(node, style, CVMX_PKI_CLUSTER_ALL, &style_cfg); +	cvmx_pki_read_qpg_entry(node, style_cfg.parm_cfg.qpg_base, &qpg_cfg); +	qpg_cfg.qpg_base = CVMX_PKI_FIND_AVAL_ENTRY; +	qpg_cfg.grp_ok = qos_watcher[index].sso_grp; +	qpg_cfg.grp_bad = qos_watcher[index].sso_grp; +	qpg_offset = cvmx_helper_pki_set_qpg_entry(node, &qpg_cfg); +	if (qpg_offset == -1) { +		debug("Warning: no new qpg entry available to enable watcher\n"); +		return -1; +	} +	/* try to reserve the style, if it is not configured already, reserve +	   and configure it */ +	style_cfg.parm_cfg.qpg_base = qpg_offset; +	style_num = cvmx_pki_style_alloc(node, -1); +	if (style_num < 0) { +		debug("ERROR: style not available to enable qos watcher\n"); +		cvmx_pki_qpg_entry_free(node, qpg_offset, 1); +		return -1; +	} +	cvmx_pki_write_style_config(node, style_num, CVMX_PKI_CLUSTER_ALL, &style_cfg); +	/* legacy will write to all clusters*/ +	bank = qos_watcher[index].field & 0x01; +	pcam_offset = cvmx_pki_pcam_entry_alloc(node, CVMX_PKI_FIND_AVAL_ENTRY, bank, +						CVMX_PKI_CLUSTER_ALL); +	if (pcam_offset < 0) { +		debug("ERROR: pcam entry not available to enable qos watcher\n"); +		cvmx_pki_style_free(node, style_num); +		cvmx_pki_qpg_entry_free(node, qpg_offset, 1); +		return -1; +	} +	memset(&pcam_input, 0, sizeof(pcam_input)); +	memset(&pcam_action, 0, sizeof(pcam_action)); +	pcam_input.style = style; +	pcam_input.style_mask = 0xff; +	pcam_input.field = qos_watcher[index].field; +	pcam_input.field_mask = 0xff; +	pcam_input.data = qos_watcher[index].data; +	pcam_input.data_mask = qos_watcher[index].data_mask; +	pcam_action.parse_mode_chg = CVMX_PKI_PARSE_NO_CHG; +	pcam_action.layer_type_set = CVMX_PKI_LTYPE_E_NONE; +	pcam_action.style_add = (u8)(style_num - style); +	pcam_action.pointer_advance = qos_watcher[index].advance; +	cvmx_pki_pcam_write_entry(node, pcam_offset, CVMX_PKI_CLUSTER_ALL, pcam_input, pcam_action); +	return 0; +} + +/** + * Configure an ethernet input port + * + * @param ipd_port Port number to configure + * @param port_cfg Port hardware configuration + * @param port_tag_cfg Port POW tagging configuration + */ +static inline void cvmx_pip_config_port(u64 ipd_port, cvmx_pip_prt_cfgx_t port_cfg, +					cvmx_pip_prt_tagx_t port_tag_cfg) +{ +	struct cvmx_pki_qpg_config qpg_cfg; +	int qpg_offset; +	u8 tcp_tag = 0xff; +	u8 ip_tag = 0xaa; +	int style, nstyle, n4style, n6style; + +	if (octeon_has_feature(OCTEON_FEATURE_PKI)) { +		struct cvmx_pki_port_config pki_prt_cfg; +		struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(ipd_port); + +		cvmx_pki_get_port_config(ipd_port, &pki_prt_cfg); +		style = pki_prt_cfg.pkind_cfg.initial_style; +		if (port_cfg.s.ih_pri || port_cfg.s.vlan_len || port_cfg.s.pad_len) +			debug("Warning: 78xx: use different config for this option\n"); +		pki_prt_cfg.style_cfg.parm_cfg.minmax_sel = port_cfg.s.len_chk_sel; +		pki_prt_cfg.style_cfg.parm_cfg.lenerr_en = port_cfg.s.lenerr_en; +		pki_prt_cfg.style_cfg.parm_cfg.maxerr_en = port_cfg.s.maxerr_en; +		pki_prt_cfg.style_cfg.parm_cfg.minerr_en = port_cfg.s.minerr_en; +		pki_prt_cfg.style_cfg.parm_cfg.fcs_chk = port_cfg.s.crc_en; +		if (port_cfg.s.grp_wat || port_cfg.s.qos_wat || port_cfg.s.grp_wat_47 || +		    port_cfg.s.qos_wat_47) { +			u8 group_mask = (u8)(port_cfg.s.grp_wat | (u8)(port_cfg.s.grp_wat_47 << 4)); +			u8 qos_mask = (u8)(port_cfg.s.qos_wat | (u8)(port_cfg.s.qos_wat_47 << 4)); +			int i; + +			for (i = 0; i < CVMX_PIP_NUM_WATCHERS; i++) { +				if ((group_mask & (1 << i)) || (qos_mask & (1 << i))) +					__cvmx_pip_enable_watcher_78xx(xp.node, i, style); +			} +		} +		if (port_tag_cfg.s.tag_mode) { +			if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) +				cvmx_printf("Warning: mask tag is not supported in 78xx pass1\n"); +			else { +			} +			/* need to implement for 78xx*/ +		} +		if (port_cfg.s.tag_inc) +			debug("Warning: 78xx uses differnet method for tag generation\n"); +		pki_prt_cfg.style_cfg.parm_cfg.rawdrp = port_cfg.s.rawdrp; +		pki_prt_cfg.pkind_cfg.parse_en.inst_hdr = port_cfg.s.inst_hdr; +		if (port_cfg.s.hg_qos) +			pki_prt_cfg.style_cfg.parm_cfg.qpg_qos = CVMX_PKI_QPG_QOS_HIGIG; +		else if (port_cfg.s.qos_vlan) +			pki_prt_cfg.style_cfg.parm_cfg.qpg_qos = CVMX_PKI_QPG_QOS_VLAN; +		else if (port_cfg.s.qos_diff) +			pki_prt_cfg.style_cfg.parm_cfg.qpg_qos = CVMX_PKI_QPG_QOS_DIFFSERV; +		if (port_cfg.s.qos_vod) +			debug("Warning: 78xx needs pcam entries installed to achieve qos_vod\n"); +		if (port_cfg.s.qos) { +			cvmx_pki_read_qpg_entry(xp.node, pki_prt_cfg.style_cfg.parm_cfg.qpg_base, +						&qpg_cfg); +			qpg_cfg.qpg_base = CVMX_PKI_FIND_AVAL_ENTRY; +			qpg_cfg.grp_ok |= port_cfg.s.qos; +			qpg_cfg.grp_bad |= port_cfg.s.qos; +			qpg_offset = cvmx_helper_pki_set_qpg_entry(xp.node, &qpg_cfg); +			if (qpg_offset == -1) +				debug("Warning: no new qpg entry available, will not modify qos\n"); +			else +				pki_prt_cfg.style_cfg.parm_cfg.qpg_base = qpg_offset; +		} +		if (port_tag_cfg.s.grp != pki_dflt_sso_grp[xp.node].group) { +			cvmx_pki_read_qpg_entry(xp.node, pki_prt_cfg.style_cfg.parm_cfg.qpg_base, +						&qpg_cfg); +			qpg_cfg.qpg_base = CVMX_PKI_FIND_AVAL_ENTRY; +			qpg_cfg.grp_ok |= (u8)(port_tag_cfg.s.grp << 3); +			qpg_cfg.grp_bad |= (u8)(port_tag_cfg.s.grp << 3); +			qpg_offset = cvmx_helper_pki_set_qpg_entry(xp.node, &qpg_cfg); +			if (qpg_offset == -1) +				debug("Warning: no new qpg entry available, will not modify group\n"); +			else +				pki_prt_cfg.style_cfg.parm_cfg.qpg_base = qpg_offset; +		} +		pki_prt_cfg.pkind_cfg.parse_en.dsa_en = port_cfg.s.dsa_en; +		pki_prt_cfg.pkind_cfg.parse_en.hg_en = port_cfg.s.higig_en; +		pki_prt_cfg.style_cfg.tag_cfg.tag_fields.layer_c_src = +			port_tag_cfg.s.ip6_src_flag | port_tag_cfg.s.ip4_src_flag; +		pki_prt_cfg.style_cfg.tag_cfg.tag_fields.layer_c_dst = +			port_tag_cfg.s.ip6_dst_flag | port_tag_cfg.s.ip4_dst_flag; +		pki_prt_cfg.style_cfg.tag_cfg.tag_fields.ip_prot_nexthdr = +			port_tag_cfg.s.ip6_nxth_flag | port_tag_cfg.s.ip4_pctl_flag; +		pki_prt_cfg.style_cfg.tag_cfg.tag_fields.layer_d_src = +			port_tag_cfg.s.ip6_sprt_flag | port_tag_cfg.s.ip4_sprt_flag; +		pki_prt_cfg.style_cfg.tag_cfg.tag_fields.layer_d_dst = +			port_tag_cfg.s.ip6_dprt_flag | port_tag_cfg.s.ip4_dprt_flag; +		pki_prt_cfg.style_cfg.tag_cfg.tag_fields.input_port = port_tag_cfg.s.inc_prt_flag; +		pki_prt_cfg.style_cfg.tag_cfg.tag_fields.first_vlan = port_tag_cfg.s.inc_vlan; +		pki_prt_cfg.style_cfg.tag_cfg.tag_fields.second_vlan = port_tag_cfg.s.inc_vs; + +		if (port_tag_cfg.s.tcp6_tag_type == port_tag_cfg.s.tcp4_tag_type) +			tcp_tag = port_tag_cfg.s.tcp6_tag_type; +		if (port_tag_cfg.s.ip6_tag_type == port_tag_cfg.s.ip4_tag_type) +			ip_tag = port_tag_cfg.s.ip6_tag_type; +		pki_prt_cfg.style_cfg.parm_cfg.tag_type = +			(enum cvmx_sso_tag_type)port_tag_cfg.s.non_tag_type; +		if (tcp_tag == ip_tag && tcp_tag == port_tag_cfg.s.non_tag_type) +			pki_prt_cfg.style_cfg.parm_cfg.tag_type = (enum cvmx_sso_tag_type)tcp_tag; +		else if (tcp_tag == ip_tag) { +			/* allocate and copy style */ +			/* modify tag type */ +			/*pcam entry for ip6 && ip4 match*/ +			/* default is non tag type */ +			__cvmx_pip_set_tag_type(xp.node, style, ip_tag, CVMX_PKI_PCAM_MATCH_IP); +		} else if (ip_tag == port_tag_cfg.s.non_tag_type) { +			/* allocate and copy style */ +			/* modify tag type */ +			/*pcam entry for tcp6 & tcp4 match*/ +			/* default is non tag type */ +			__cvmx_pip_set_tag_type(xp.node, style, tcp_tag, CVMX_PKI_PCAM_MATCH_TCP); +		} else { +			if (ip_tag != 0xaa) { +				nstyle = __cvmx_pip_set_tag_type(xp.node, style, ip_tag, +								 CVMX_PKI_PCAM_MATCH_IP); +				if (tcp_tag != 0xff) +					__cvmx_pip_set_tag_type(xp.node, nstyle, tcp_tag, +								CVMX_PKI_PCAM_MATCH_TCP); +				else { +					n4style = __cvmx_pip_set_tag_type(xp.node, nstyle, ip_tag, +									  CVMX_PKI_PCAM_MATCH_IPV4); +					__cvmx_pip_set_tag_type(xp.node, n4style, +								port_tag_cfg.s.tcp4_tag_type, +								CVMX_PKI_PCAM_MATCH_TCP); +					n6style = __cvmx_pip_set_tag_type(xp.node, nstyle, ip_tag, +									  CVMX_PKI_PCAM_MATCH_IPV6); +					__cvmx_pip_set_tag_type(xp.node, n6style, +								port_tag_cfg.s.tcp6_tag_type, +								CVMX_PKI_PCAM_MATCH_TCP); +				} +			} else { +				n4style = __cvmx_pip_set_tag_type(xp.node, style, +								  port_tag_cfg.s.ip4_tag_type, +								  CVMX_PKI_PCAM_MATCH_IPV4); +				n6style = __cvmx_pip_set_tag_type(xp.node, style, +								  port_tag_cfg.s.ip6_tag_type, +								  CVMX_PKI_PCAM_MATCH_IPV6); +				if (tcp_tag != 0xff) { +					__cvmx_pip_set_tag_type(xp.node, n4style, tcp_tag, +								CVMX_PKI_PCAM_MATCH_TCP); +					__cvmx_pip_set_tag_type(xp.node, n6style, tcp_tag, +								CVMX_PKI_PCAM_MATCH_TCP); +				} else { +					__cvmx_pip_set_tag_type(xp.node, n4style, +								port_tag_cfg.s.tcp4_tag_type, +								CVMX_PKI_PCAM_MATCH_TCP); +					__cvmx_pip_set_tag_type(xp.node, n6style, +								port_tag_cfg.s.tcp6_tag_type, +								CVMX_PKI_PCAM_MATCH_TCP); +				} +			} +		} +		pki_prt_cfg.style_cfg.parm_cfg.qpg_dis_padd = !port_tag_cfg.s.portadd_en; + +		if (port_cfg.s.mode == 0x1) +			pki_prt_cfg.pkind_cfg.initial_parse_mode = CVMX_PKI_PARSE_LA_TO_LG; +		else if (port_cfg.s.mode == 0x2) +			pki_prt_cfg.pkind_cfg.initial_parse_mode = CVMX_PKI_PARSE_LC_TO_LG; +		else +			pki_prt_cfg.pkind_cfg.initial_parse_mode = CVMX_PKI_PARSE_NOTHING; +		/* This is only for backward compatibility, not all the parameters are supported in 78xx */ +		cvmx_pki_set_port_config(ipd_port, &pki_prt_cfg); +	} else { +		if (octeon_has_feature(OCTEON_FEATURE_PKND)) { +			int interface, index, pknd; + +			interface = cvmx_helper_get_interface_num(ipd_port); +			index = cvmx_helper_get_interface_index_num(ipd_port); +			pknd = cvmx_helper_get_pknd(interface, index); + +			ipd_port = pknd; /* overload port_num with pknd */ +		} +		csr_wr(CVMX_PIP_PRT_CFGX(ipd_port), port_cfg.u64); +		csr_wr(CVMX_PIP_PRT_TAGX(ipd_port), port_tag_cfg.u64); +	} +} + +/** + * Configure the VLAN priority to QoS queue mapping. + * + * @param vlan_priority + *               VLAN priority (0-7) + * @param qos    QoS queue for packets matching this watcher + */ +static inline void cvmx_pip_config_vlan_qos(u64 vlan_priority, u64 qos) +{ +	if (!octeon_has_feature(OCTEON_FEATURE_PKND)) { +		cvmx_pip_qos_vlanx_t pip_qos_vlanx; + +		pip_qos_vlanx.u64 = 0; +		pip_qos_vlanx.s.qos = qos; +		csr_wr(CVMX_PIP_QOS_VLANX(vlan_priority), pip_qos_vlanx.u64); +	} +} + +/** + * Configure the Diffserv to QoS queue mapping. + * + * @param diffserv Diffserv field value (0-63) + * @param qos      QoS queue for packets matching this watcher + */ +static inline void cvmx_pip_config_diffserv_qos(u64 diffserv, u64 qos) +{ +	if (!octeon_has_feature(OCTEON_FEATURE_PKND)) { +		cvmx_pip_qos_diffx_t pip_qos_diffx; + +		pip_qos_diffx.u64 = 0; +		pip_qos_diffx.s.qos = qos; +		csr_wr(CVMX_PIP_QOS_DIFFX(diffserv), pip_qos_diffx.u64); +	} +} + +/** + * Get the status counters for a port for older non PKI chips. + * + * @param port_num Port number (ipd_port) to get statistics for. + * @param clear    Set to 1 to clear the counters after they are read + * @param status   Where to put the results. + */ +static inline void cvmx_pip_get_port_stats(u64 port_num, u64 clear, cvmx_pip_port_status_t *status) +{ +	cvmx_pip_stat_ctl_t pip_stat_ctl; +	cvmx_pip_stat0_prtx_t stat0; +	cvmx_pip_stat1_prtx_t stat1; +	cvmx_pip_stat2_prtx_t stat2; +	cvmx_pip_stat3_prtx_t stat3; +	cvmx_pip_stat4_prtx_t stat4; +	cvmx_pip_stat5_prtx_t stat5; +	cvmx_pip_stat6_prtx_t stat6; +	cvmx_pip_stat7_prtx_t stat7; +	cvmx_pip_stat8_prtx_t stat8; +	cvmx_pip_stat9_prtx_t stat9; +	cvmx_pip_stat10_x_t stat10; +	cvmx_pip_stat11_x_t stat11; +	cvmx_pip_stat_inb_pktsx_t pip_stat_inb_pktsx; +	cvmx_pip_stat_inb_octsx_t pip_stat_inb_octsx; +	cvmx_pip_stat_inb_errsx_t pip_stat_inb_errsx; +	int interface = cvmx_helper_get_interface_num(port_num); +	int index = cvmx_helper_get_interface_index_num(port_num); + +	pip_stat_ctl.u64 = 0; +	pip_stat_ctl.s.rdclr = clear; +	csr_wr(CVMX_PIP_STAT_CTL, pip_stat_ctl.u64); + +	if (octeon_has_feature(OCTEON_FEATURE_PKND)) { +		int pknd = cvmx_helper_get_pknd(interface, index); +		/* +		 * PIP_STAT_CTL[MODE] 0 means pkind. +		 */ +		stat0.u64 = csr_rd(CVMX_PIP_STAT0_X(pknd)); +		stat1.u64 = csr_rd(CVMX_PIP_STAT1_X(pknd)); +		stat2.u64 = csr_rd(CVMX_PIP_STAT2_X(pknd)); +		stat3.u64 = csr_rd(CVMX_PIP_STAT3_X(pknd)); +		stat4.u64 = csr_rd(CVMX_PIP_STAT4_X(pknd)); +		stat5.u64 = csr_rd(CVMX_PIP_STAT5_X(pknd)); +		stat6.u64 = csr_rd(CVMX_PIP_STAT6_X(pknd)); +		stat7.u64 = csr_rd(CVMX_PIP_STAT7_X(pknd)); +		stat8.u64 = csr_rd(CVMX_PIP_STAT8_X(pknd)); +		stat9.u64 = csr_rd(CVMX_PIP_STAT9_X(pknd)); +		stat10.u64 = csr_rd(CVMX_PIP_STAT10_X(pknd)); +		stat11.u64 = csr_rd(CVMX_PIP_STAT11_X(pknd)); +	} else { +		if (port_num >= 40) { +			stat0.u64 = csr_rd(CVMX_PIP_XSTAT0_PRTX(port_num)); +			stat1.u64 = csr_rd(CVMX_PIP_XSTAT1_PRTX(port_num)); +			stat2.u64 = csr_rd(CVMX_PIP_XSTAT2_PRTX(port_num)); +			stat3.u64 = csr_rd(CVMX_PIP_XSTAT3_PRTX(port_num)); +			stat4.u64 = csr_rd(CVMX_PIP_XSTAT4_PRTX(port_num)); +			stat5.u64 = csr_rd(CVMX_PIP_XSTAT5_PRTX(port_num)); +			stat6.u64 = csr_rd(CVMX_PIP_XSTAT6_PRTX(port_num)); +			stat7.u64 = csr_rd(CVMX_PIP_XSTAT7_PRTX(port_num)); +			stat8.u64 = csr_rd(CVMX_PIP_XSTAT8_PRTX(port_num)); +			stat9.u64 = csr_rd(CVMX_PIP_XSTAT9_PRTX(port_num)); +			if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { +				stat10.u64 = csr_rd(CVMX_PIP_XSTAT10_PRTX(port_num)); +				stat11.u64 = csr_rd(CVMX_PIP_XSTAT11_PRTX(port_num)); +			} +		} else { +			stat0.u64 = csr_rd(CVMX_PIP_STAT0_PRTX(port_num)); +			stat1.u64 = csr_rd(CVMX_PIP_STAT1_PRTX(port_num)); +			stat2.u64 = csr_rd(CVMX_PIP_STAT2_PRTX(port_num)); +			stat3.u64 = csr_rd(CVMX_PIP_STAT3_PRTX(port_num)); +			stat4.u64 = csr_rd(CVMX_PIP_STAT4_PRTX(port_num)); +			stat5.u64 = csr_rd(CVMX_PIP_STAT5_PRTX(port_num)); +			stat6.u64 = csr_rd(CVMX_PIP_STAT6_PRTX(port_num)); +			stat7.u64 = csr_rd(CVMX_PIP_STAT7_PRTX(port_num)); +			stat8.u64 = csr_rd(CVMX_PIP_STAT8_PRTX(port_num)); +			stat9.u64 = csr_rd(CVMX_PIP_STAT9_PRTX(port_num)); +			if (OCTEON_IS_OCTEON2() || OCTEON_IS_MODEL(OCTEON_CN70XX)) { +				stat10.u64 = csr_rd(CVMX_PIP_STAT10_PRTX(port_num)); +				stat11.u64 = csr_rd(CVMX_PIP_STAT11_PRTX(port_num)); +			} +		} +	} +	if (octeon_has_feature(OCTEON_FEATURE_PKND)) { +		int pknd = cvmx_helper_get_pknd(interface, index); + +		pip_stat_inb_pktsx.u64 = csr_rd(CVMX_PIP_STAT_INB_PKTS_PKNDX(pknd)); +		pip_stat_inb_octsx.u64 = csr_rd(CVMX_PIP_STAT_INB_OCTS_PKNDX(pknd)); +		pip_stat_inb_errsx.u64 = csr_rd(CVMX_PIP_STAT_INB_ERRS_PKNDX(pknd)); +	} else { +		pip_stat_inb_pktsx.u64 = csr_rd(CVMX_PIP_STAT_INB_PKTSX(port_num)); +		pip_stat_inb_octsx.u64 = csr_rd(CVMX_PIP_STAT_INB_OCTSX(port_num)); +		pip_stat_inb_errsx.u64 = csr_rd(CVMX_PIP_STAT_INB_ERRSX(port_num)); +	} + +	status->dropped_octets = stat0.s.drp_octs; +	status->dropped_packets = stat0.s.drp_pkts; +	status->octets = stat1.s.octs; +	status->pci_raw_packets = stat2.s.raw; +	status->packets = stat2.s.pkts; +	status->multicast_packets = stat3.s.mcst; +	status->broadcast_packets = stat3.s.bcst; +	status->len_64_packets = stat4.s.h64; +	status->len_65_127_packets = stat4.s.h65to127; +	status->len_128_255_packets = stat5.s.h128to255; +	status->len_256_511_packets = stat5.s.h256to511; +	status->len_512_1023_packets = stat6.s.h512to1023; +	status->len_1024_1518_packets = stat6.s.h1024to1518; +	status->len_1519_max_packets = stat7.s.h1519; +	status->fcs_align_err_packets = stat7.s.fcs; +	status->runt_packets = stat8.s.undersz; +	status->runt_crc_packets = stat8.s.frag; +	status->oversize_packets = stat9.s.oversz; +	status->oversize_crc_packets = stat9.s.jabber; +	if (OCTEON_IS_OCTEON2() || OCTEON_IS_MODEL(OCTEON_CN70XX)) { +		status->mcast_l2_red_packets = stat10.s.mcast; +		status->bcast_l2_red_packets = stat10.s.bcast; +		status->mcast_l3_red_packets = stat11.s.mcast; +		status->bcast_l3_red_packets = stat11.s.bcast; +	} +	status->inb_packets = pip_stat_inb_pktsx.s.pkts; +	status->inb_octets = pip_stat_inb_octsx.s.octs; +	status->inb_errors = pip_stat_inb_errsx.s.errs; +} + +/** + * Get the status counters for a port. + * + * @param port_num Port number (ipd_port) to get statistics for. + * @param clear    Set to 1 to clear the counters after they are read + * @param status   Where to put the results. + */ +static inline void cvmx_pip_get_port_status(u64 port_num, u64 clear, cvmx_pip_port_status_t *status) +{ +	if (octeon_has_feature(OCTEON_FEATURE_PKI)) { +		unsigned int node = cvmx_get_node_num(); + +		cvmx_pki_get_port_stats(node, port_num, (struct cvmx_pki_port_stats *)status); +	} else { +		cvmx_pip_get_port_stats(port_num, clear, status); +	} +} + +/** + * Configure the hardware CRC engine + * + * @param interface Interface to configure (0 or 1) + * @param invert_result + *                 Invert the result of the CRC + * @param reflect  Reflect + * @param initialization_vector + *                 CRC initialization vector + */ +static inline void cvmx_pip_config_crc(u64 interface, u64 invert_result, u64 reflect, +				       u32 initialization_vector) +{ +	/* Only CN38XX & CN58XX */ +} + +/** + * Clear all bits in a tag mask. This should be called on + * startup before any calls to cvmx_pip_tag_mask_set. Each bit + * set in the final mask represent a byte used in the packet for + * tag generation. + * + * @param mask_index Which tag mask to clear (0..3) + */ +static inline void cvmx_pip_tag_mask_clear(u64 mask_index) +{ +	u64 index; +	cvmx_pip_tag_incx_t pip_tag_incx; + +	pip_tag_incx.u64 = 0; +	pip_tag_incx.s.en = 0; +	for (index = mask_index * 16; index < (mask_index + 1) * 16; index++) +		csr_wr(CVMX_PIP_TAG_INCX(index), pip_tag_incx.u64); +} + +/** + * Sets a range of bits in the tag mask. The tag mask is used + * when the cvmx_pip_port_tag_cfg_t tag_mode is non zero. + * There are four separate masks that can be configured. + * + * @param mask_index Which tag mask to modify (0..3) + * @param offset     Offset into the bitmask to set bits at. Use the GCC macro + *                   offsetof() to determine the offsets into packet headers. + *                   For example, offsetof(ethhdr, protocol) returns the offset + *                   of the ethernet protocol field.  The bitmask selects which bytes + *                   to include the the tag, with bit offset X selecting byte at offset X + *                   from the beginning of the packet data. + * @param len        Number of bytes to include. Usually this is the sizeof() + *                   the field. + */ +static inline void cvmx_pip_tag_mask_set(u64 mask_index, u64 offset, u64 len) +{ +	while (len--) { +		cvmx_pip_tag_incx_t pip_tag_incx; +		u64 index = mask_index * 16 + offset / 8; + +		pip_tag_incx.u64 = csr_rd(CVMX_PIP_TAG_INCX(index)); +		pip_tag_incx.s.en |= 0x80 >> (offset & 0x7); +		csr_wr(CVMX_PIP_TAG_INCX(index), pip_tag_incx.u64); +		offset++; +	} +} + +/** + * Set byte count for Max-Sized and Min Sized frame check. + * + * @param interface   Which interface to set the limit + * @param max_size    Byte count for Max-Size frame check + */ +static inline void cvmx_pip_set_frame_check(int interface, u32 max_size) +{ +	cvmx_pip_frm_len_chkx_t frm_len; + +	/* max_size and min_size are passed as 0, reset to default values. */ +	if (max_size < 1536) +		max_size = 1536; + +	/* On CN68XX frame check is enabled for a pkind n and +	   PIP_PRT_CFG[len_chk_sel] selects which set of +	   MAXLEN/MINLEN to use. */ +	if (octeon_has_feature(OCTEON_FEATURE_PKND)) { +		int port; +		int num_ports = cvmx_helper_ports_on_interface(interface); + +		for (port = 0; port < num_ports; port++) { +			if (octeon_has_feature(OCTEON_FEATURE_PKI)) { +				int ipd_port; + +				ipd_port = cvmx_helper_get_ipd_port(interface, port); +				cvmx_pki_set_max_frm_len(ipd_port, max_size); +			} else { +				int pknd; +				int sel; +				cvmx_pip_prt_cfgx_t config; + +				pknd = cvmx_helper_get_pknd(interface, port); +				config.u64 = csr_rd(CVMX_PIP_PRT_CFGX(pknd)); +				sel = config.s.len_chk_sel; +				frm_len.u64 = csr_rd(CVMX_PIP_FRM_LEN_CHKX(sel)); +				frm_len.s.maxlen = max_size; +				csr_wr(CVMX_PIP_FRM_LEN_CHKX(sel), frm_len.u64); +			} +		} +	} +	/* on cn6xxx and cn7xxx models, PIP_FRM_LEN_CHK0 applies to +	 *     all incoming traffic */ +	else if (OCTEON_IS_OCTEON2() || OCTEON_IS_MODEL(OCTEON_CN70XX)) { +		frm_len.u64 = csr_rd(CVMX_PIP_FRM_LEN_CHKX(0)); +		frm_len.s.maxlen = max_size; +		csr_wr(CVMX_PIP_FRM_LEN_CHKX(0), frm_len.u64); +	} +} + +/** + * Initialize Bit Select Extractor config. Their are 8 bit positions and valids + * to be used when using the corresponding extractor. + * + * @param bit     Bit Select Extractor to use + * @param pos     Which position to update + * @param val     The value to update the position with + */ +static inline void cvmx_pip_set_bsel_pos(int bit, int pos, int val) +{ +	cvmx_pip_bsel_ext_posx_t bsel_pos; + +	/* The bit select extractor is available in CN61XX and CN68XX pass2.0 onwards. */ +	if (!octeon_has_feature(OCTEON_FEATURE_BIT_EXTRACTOR)) +		return; + +	if (bit < 0 || bit > 3) { +		debug("ERROR: cvmx_pip_set_bsel_pos: Invalid Bit-Select Extractor (%d) passed\n", +		      bit); +		return; +	} + +	bsel_pos.u64 = csr_rd(CVMX_PIP_BSEL_EXT_POSX(bit)); +	switch (pos) { +	case 0: +		bsel_pos.s.pos0_val = 1; +		bsel_pos.s.pos0 = val & 0x7f; +		break; +	case 1: +		bsel_pos.s.pos1_val = 1; +		bsel_pos.s.pos1 = val & 0x7f; +		break; +	case 2: +		bsel_pos.s.pos2_val = 1; +		bsel_pos.s.pos2 = val & 0x7f; +		break; +	case 3: +		bsel_pos.s.pos3_val = 1; +		bsel_pos.s.pos3 = val & 0x7f; +		break; +	case 4: +		bsel_pos.s.pos4_val = 1; +		bsel_pos.s.pos4 = val & 0x7f; +		break; +	case 5: +		bsel_pos.s.pos5_val = 1; +		bsel_pos.s.pos5 = val & 0x7f; +		break; +	case 6: +		bsel_pos.s.pos6_val = 1; +		bsel_pos.s.pos6 = val & 0x7f; +		break; +	case 7: +		bsel_pos.s.pos7_val = 1; +		bsel_pos.s.pos7 = val & 0x7f; +		break; +	default: +		debug("Warning: cvmx_pip_set_bsel_pos: Invalid pos(%d)\n", pos); +		break; +	} +	csr_wr(CVMX_PIP_BSEL_EXT_POSX(bit), bsel_pos.u64); +} + +/** + * Initialize offset and skip values to use by bit select extractor. + + * @param bit	Bit Select Extractor to use + * @param offset	Offset to add to extractor mem addr to get final address + *			to lookup table. + * @param skip		Number of bytes to skip from start of packet 0-64 + */ +static inline void cvmx_pip_bsel_config(int bit, int offset, int skip) +{ +	cvmx_pip_bsel_ext_cfgx_t bsel_cfg; + +	/* The bit select extractor is available in CN61XX and CN68XX pass2.0 onwards. */ +	if (!octeon_has_feature(OCTEON_FEATURE_BIT_EXTRACTOR)) +		return; + +	bsel_cfg.u64 = csr_rd(CVMX_PIP_BSEL_EXT_CFGX(bit)); +	bsel_cfg.s.offset = offset; +	bsel_cfg.s.skip = skip; +	csr_wr(CVMX_PIP_BSEL_EXT_CFGX(bit), bsel_cfg.u64); +} + +/** + * Get the entry for the Bit Select Extractor Table. + * @param work   pointer to work queue entry + * @return       Index of the Bit Select Extractor Table + */ +static inline int cvmx_pip_get_bsel_table_index(cvmx_wqe_t *work) +{ +	int bit = cvmx_wqe_get_port(work) & 0x3; +	/* Get the Bit select table index. */ +	int index; +	int y; +	cvmx_pip_bsel_ext_cfgx_t bsel_cfg; +	cvmx_pip_bsel_ext_posx_t bsel_pos; + +	/* The bit select extractor is available in CN61XX and CN68XX pass2.0 onwards. */ +	if (!octeon_has_feature(OCTEON_FEATURE_BIT_EXTRACTOR)) +		return -1; + +	bsel_cfg.u64 = csr_rd(CVMX_PIP_BSEL_EXT_CFGX(bit)); +	bsel_pos.u64 = csr_rd(CVMX_PIP_BSEL_EXT_POSX(bit)); + +	for (y = 0; y < 8; y++) { +		char *ptr = (char *)cvmx_phys_to_ptr(work->packet_ptr.s.addr); +		int bit_loc = 0; +		int bit; + +		ptr += bsel_cfg.s.skip; +		switch (y) { +		case 0: +			ptr += (bsel_pos.s.pos0 >> 3); +			bit_loc = 7 - (bsel_pos.s.pos0 & 0x3); +			break; +		case 1: +			ptr += (bsel_pos.s.pos1 >> 3); +			bit_loc = 7 - (bsel_pos.s.pos1 & 0x3); +			break; +		case 2: +			ptr += (bsel_pos.s.pos2 >> 3); +			bit_loc = 7 - (bsel_pos.s.pos2 & 0x3); +			break; +		case 3: +			ptr += (bsel_pos.s.pos3 >> 3); +			bit_loc = 7 - (bsel_pos.s.pos3 & 0x3); +			break; +		case 4: +			ptr += (bsel_pos.s.pos4 >> 3); +			bit_loc = 7 - (bsel_pos.s.pos4 & 0x3); +			break; +		case 5: +			ptr += (bsel_pos.s.pos5 >> 3); +			bit_loc = 7 - (bsel_pos.s.pos5 & 0x3); +			break; +		case 6: +			ptr += (bsel_pos.s.pos6 >> 3); +			bit_loc = 7 - (bsel_pos.s.pos6 & 0x3); +			break; +		case 7: +			ptr += (bsel_pos.s.pos7 >> 3); +			bit_loc = 7 - (bsel_pos.s.pos7 & 0x3); +			break; +		} +		bit = (*ptr >> bit_loc) & 1; +		index |= bit << y; +	} +	index += bsel_cfg.s.offset; +	index &= 0x1ff; +	return index; +} + +static inline int cvmx_pip_get_bsel_qos(cvmx_wqe_t *work) +{ +	int index = cvmx_pip_get_bsel_table_index(work); +	cvmx_pip_bsel_tbl_entx_t bsel_tbl; + +	/* The bit select extractor is available in CN61XX and CN68XX pass2.0 onwards. */ +	if (!octeon_has_feature(OCTEON_FEATURE_BIT_EXTRACTOR)) +		return -1; + +	bsel_tbl.u64 = csr_rd(CVMX_PIP_BSEL_TBL_ENTX(index)); + +	return bsel_tbl.s.qos; +} + +static inline int cvmx_pip_get_bsel_grp(cvmx_wqe_t *work) +{ +	int index = cvmx_pip_get_bsel_table_index(work); +	cvmx_pip_bsel_tbl_entx_t bsel_tbl; + +	/* The bit select extractor is available in CN61XX and CN68XX pass2.0 onwards. */ +	if (!octeon_has_feature(OCTEON_FEATURE_BIT_EXTRACTOR)) +		return -1; + +	bsel_tbl.u64 = csr_rd(CVMX_PIP_BSEL_TBL_ENTX(index)); + +	return bsel_tbl.s.grp; +} + +static inline int cvmx_pip_get_bsel_tt(cvmx_wqe_t *work) +{ +	int index = cvmx_pip_get_bsel_table_index(work); +	cvmx_pip_bsel_tbl_entx_t bsel_tbl; + +	/* The bit select extractor is available in CN61XX and CN68XX pass2.0 onwards. */ +	if (!octeon_has_feature(OCTEON_FEATURE_BIT_EXTRACTOR)) +		return -1; + +	bsel_tbl.u64 = csr_rd(CVMX_PIP_BSEL_TBL_ENTX(index)); + +	return bsel_tbl.s.tt; +} + +static inline int cvmx_pip_get_bsel_tag(cvmx_wqe_t *work) +{ +	int index = cvmx_pip_get_bsel_table_index(work); +	int port = cvmx_wqe_get_port(work); +	int bit = port & 0x3; +	int upper_tag = 0; +	cvmx_pip_bsel_tbl_entx_t bsel_tbl; +	cvmx_pip_bsel_ext_cfgx_t bsel_cfg; +	cvmx_pip_prt_tagx_t prt_tag; + +	/* The bit select extractor is available in CN61XX and CN68XX pass2.0 onwards. */ +	if (!octeon_has_feature(OCTEON_FEATURE_BIT_EXTRACTOR)) +		return -1; + +	bsel_tbl.u64 = csr_rd(CVMX_PIP_BSEL_TBL_ENTX(index)); +	bsel_cfg.u64 = csr_rd(CVMX_PIP_BSEL_EXT_CFGX(bit)); + +	prt_tag.u64 = csr_rd(CVMX_PIP_PRT_TAGX(port)); +	if (prt_tag.s.inc_prt_flag == 0) +		upper_tag = bsel_cfg.s.upper_tag; +	return bsel_tbl.s.tag | ((bsel_cfg.s.tag << 8) & 0xff00) | ((upper_tag << 16) & 0xffff0000); +} + +#endif /*  __CVMX_PIP_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pki-resources.h b/arch/mips/mach-octeon/include/mach/cvmx-pki-resources.h new file mode 100644 index 00000000000..79b99b0bd7c --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pki-resources.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Resource management for PKI resources. + */ + +#ifndef __CVMX_PKI_RESOURCES_H__ +#define __CVMX_PKI_RESOURCES_H__ + +/** + * This function allocates/reserves a style from pool of global styles per node. + * @param node	 node to allocate style from. + * @param style	 style to allocate, if -1 it will be allocated +		 first available style from style resource. If index is positive +		 number and in range, it will try to allocate specified style. + * @return	 style number on success, -1 on failure. + */ +int cvmx_pki_style_alloc(int node, int style); + +/** + * This function allocates/reserves a cluster group from per node +   cluster group resources. + * @param node		node to allocate cluster group from. +   @param cl_grp	cluster group to allocate/reserve, if -1 , +			allocate any available cluster group. + * @return		cluster group number or -1 on failure + */ +int cvmx_pki_cluster_grp_alloc(int node, int cl_grp); + +/** + * This function allocates/reserves a cluster from per node +   cluster resources. + * @param node		node to allocate cluster group from. +   @param cluster_mask	mask of clusters  to allocate/reserve, if -1 , +			allocate any available clusters. + * @param num_clusters	number of clusters that will be allocated + */ +int cvmx_pki_cluster_alloc(int node, int num_clusters, u64 *cluster_mask); + +/** + * This function allocates/reserves a pcam entry from node + * @param node		node to allocate pcam entry from. +   @param index	index of pacm entry (0-191), if -1 , +			allocate any available pcam entry. + * @param bank		pcam bank where to allocate/reserve pcan entry from + * @param cluster_mask  mask of clusters from which pcam entry is needed. + * @return		pcam entry of -1 on failure + */ +int cvmx_pki_pcam_entry_alloc(int node, int index, int bank, u64 cluster_mask); + +/** + * This function allocates/reserves QPG table entries per node. + * @param node		node number. + * @param base_offset	base_offset in qpg table. If -1, first available +			qpg base_offset will be allocated. If base_offset is positive +			number and in range, it will try to allocate specified base_offset. +   @param count		number of consecutive qpg entries to allocate. They will be consecutive +			from base offset. + * @return		qpg table base offset number on success, -1 on failure. + */ +int cvmx_pki_qpg_entry_alloc(int node, int base_offset, int count); + +/** + * This function frees a style from pool of global styles per node. + * @param node	 node to free style from. + * @param style	 style to free + * @return	 0 on success, -1 on failure. + */ +int cvmx_pki_style_free(int node, int style); + +/** + * This function frees a cluster group from per node +   cluster group resources. + * @param node		node to free cluster group from. +   @param cl_grp	cluster group to free + * @return		0 on success or -1 on failure + */ +int cvmx_pki_cluster_grp_free(int node, int cl_grp); + +/** + * This function frees QPG table entries per node. + * @param node		node number. + * @param base_offset	base_offset in qpg table. If -1, first available + *			qpg base_offset will be allocated. If base_offset is positive + *			number and in range, it will try to allocate specified base_offset. + * @param count		number of consecutive qpg entries to allocate. They will be consecutive + *			from base offset. + * @return		qpg table base offset number on success, -1 on failure. + */ +int cvmx_pki_qpg_entry_free(int node, int base_offset, int count); + +/** + * This function frees  clusters  from per node +   clusters resources. + * @param node		node to free clusters from. + * @param cluster_mask  mask of clusters need freeing + * @return		0 on success or -1 on failure + */ +int cvmx_pki_cluster_free(int node, u64 cluster_mask); + +/** + * This function frees a pcam entry from node + * @param node		node to allocate pcam entry from. +   @param index	index of pacm entry (0-191) needs to be freed. + * @param bank		pcam bank where to free pcam entry from + * @param cluster_mask  mask of clusters from which pcam entry is freed. + * @return		0 on success OR -1 on failure + */ +int cvmx_pki_pcam_entry_free(int node, int index, int bank, u64 cluster_mask); + +/** + * This function allocates/reserves a bpid from pool of global bpid per node. + * @param node	node to allocate bpid from. + * @param bpid	bpid  to allocate, if -1 it will be allocated + *		first available boid from bpid resource. If index is positive + *		number and in range, it will try to allocate specified bpid. + * @return	bpid number on success, + *		-1 on alloc failure. + *		-2 on resource already reserved. + */ +int cvmx_pki_bpid_alloc(int node, int bpid); + +/** + * This function frees a bpid from pool of global bpid per node. + * @param node	 node to free bpid from. + * @param bpid	 bpid to free + * @return	 0 on success, -1 on failure or + */ +int cvmx_pki_bpid_free(int node, int bpid); + +/** + * This function frees all the PKI software resources + * (clusters, styles, qpg_entry, pcam_entry etc) for the specified node + */ + +/** + * This function allocates/reserves an index from pool of global MTAG-IDX per node. + * @param node	node to allocate index from. + * @param idx	index  to allocate, if -1 it will be allocated + * @return	MTAG index number on success, + *		-1 on alloc failure. + *		-2 on resource already reserved. + */ +int cvmx_pki_mtag_idx_alloc(int node, int idx); + +/** + * This function frees an index from pool of global MTAG-IDX per node. + * @param node	 node to free bpid from. + * @param bpid	 bpid to free + * @return	 0 on success, -1 on failure or + */ +int cvmx_pki_mtag_idx_free(int node, int idx); + +void __cvmx_pki_global_rsrc_free(int node); + +#endif /*  __CVM_PKI_RESOURCES_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pki.h b/arch/mips/mach-octeon/include/mach/cvmx-pki.h new file mode 100644 index 00000000000..c1feb55a1f0 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pki.h @@ -0,0 +1,970 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Interface to the hardware Packet Input Data unit. + */ + +#ifndef __CVMX_PKI_H__ +#define __CVMX_PKI_H__ + +#include "cvmx-fpa3.h" +#include "cvmx-helper-util.h" +#include "cvmx-helper-cfg.h" +#include "cvmx-error.h" + +/* PKI AURA and BPID count are equal to FPA AURA count */ +#define CVMX_PKI_NUM_AURA	       (cvmx_fpa3_num_auras()) +#define CVMX_PKI_NUM_BPID	       (cvmx_fpa3_num_auras()) +#define CVMX_PKI_NUM_SSO_GROUP	       (cvmx_sso_num_xgrp()) +#define CVMX_PKI_NUM_CLUSTER_GROUP_MAX 1 +#define CVMX_PKI_NUM_CLUSTER_GROUP     (cvmx_pki_num_cl_grp()) +#define CVMX_PKI_NUM_CLUSTER	       (cvmx_pki_num_clusters()) + +/* FIXME: Reduce some of these values, convert to routines XXX */ +#define CVMX_PKI_NUM_CHANNEL	    4096 +#define CVMX_PKI_NUM_PKIND	    64 +#define CVMX_PKI_NUM_INTERNAL_STYLE 256 +#define CVMX_PKI_NUM_FINAL_STYLE    64 +#define CVMX_PKI_NUM_QPG_ENTRY	    2048 +#define CVMX_PKI_NUM_MTAG_IDX	    (32 / 4) /* 32 registers grouped by 4*/ +#define CVMX_PKI_NUM_LTYPE	    32 +#define CVMX_PKI_NUM_PCAM_BANK	    2 +#define CVMX_PKI_NUM_PCAM_ENTRY	    192 +#define CVMX_PKI_NUM_FRAME_CHECK    2 +#define CVMX_PKI_NUM_BELTYPE	    32 +#define CVMX_PKI_MAX_FRAME_SIZE	    65535 +#define CVMX_PKI_FIND_AVAL_ENTRY    (-1) +#define CVMX_PKI_CLUSTER_ALL	    0xf + +#ifdef CVMX_SUPPORT_SEPARATE_CLUSTER_CONFIG +#define CVMX_PKI_TOTAL_PCAM_ENTRY                                                                  \ +	((CVMX_PKI_NUM_CLUSTER) * (CVMX_PKI_NUM_PCAM_BANK) * (CVMX_PKI_NUM_PCAM_ENTRY)) +#else +#define CVMX_PKI_TOTAL_PCAM_ENTRY (CVMX_PKI_NUM_PCAM_BANK * CVMX_PKI_NUM_PCAM_ENTRY) +#endif + +static inline unsigned int cvmx_pki_num_clusters(void) +{ +	if (OCTEON_IS_MODEL(OCTEON_CN73XX) || OCTEON_IS_MODEL(OCTEON_CNF75XX)) +		return 2; +	return 4; +} + +static inline unsigned int cvmx_pki_num_cl_grp(void) +{ +	if (OCTEON_IS_MODEL(OCTEON_CN73XX) || OCTEON_IS_MODEL(OCTEON_CNF75XX) || +	    OCTEON_IS_MODEL(OCTEON_CN78XX)) +		return 1; +	return 0; +} + +enum cvmx_pki_pkind_parse_mode { +	CVMX_PKI_PARSE_LA_TO_LG = 0,  /* Parse LA(L2) to LG */ +	CVMX_PKI_PARSE_LB_TO_LG = 1,  /* Parse LB(custom) to LG */ +	CVMX_PKI_PARSE_LC_TO_LG = 3,  /* Parse LC(L3) to LG */ +	CVMX_PKI_PARSE_LG = 0x3f,     /* Parse LG */ +	CVMX_PKI_PARSE_NOTHING = 0x7f /* Parse nothing */ +}; + +enum cvmx_pki_parse_mode_chg { +	CVMX_PKI_PARSE_NO_CHG = 0x0, +	CVMX_PKI_PARSE_SKIP_TO_LB = 0x1, +	CVMX_PKI_PARSE_SKIP_TO_LC = 0x3, +	CVMX_PKI_PARSE_SKIP_TO_LD = 0x7, +	CVMX_PKI_PARSE_SKIP_TO_LG = 0x3f, +	CVMX_PKI_PARSE_SKIP_ALL = 0x7f, +}; + +enum cvmx_pki_l2_len_mode { PKI_L2_LENCHK_EQUAL_GREATER = 0, PKI_L2_LENCHK_EQUAL_ONLY }; + +enum cvmx_pki_cache_mode { +	CVMX_PKI_OPC_MODE_STT = 0LL,	  /* All blocks write through DRAM,*/ +	CVMX_PKI_OPC_MODE_STF = 1LL,	  /* All blocks into L2 */ +	CVMX_PKI_OPC_MODE_STF1_STT = 2LL, /* 1st block L2, rest DRAM */ +	CVMX_PKI_OPC_MODE_STF2_STT = 3LL  /* 1st, 2nd blocks L2, rest DRAM */ +}; + +/** + * Tag type definitions + */ +enum cvmx_sso_tag_type { +	CVMX_SSO_TAG_TYPE_ORDERED = 0L, +	CVMX_SSO_TAG_TYPE_ATOMIC = 1L, +	CVMX_SSO_TAG_TYPE_UNTAGGED = 2L, +	CVMX_SSO_TAG_TYPE_EMPTY = 3L +}; + +enum cvmx_pki_qpg_qos { +	CVMX_PKI_QPG_QOS_NONE = 0, +	CVMX_PKI_QPG_QOS_VLAN, +	CVMX_PKI_QPG_QOS_MPLS, +	CVMX_PKI_QPG_QOS_DSA_SRC, +	CVMX_PKI_QPG_QOS_DIFFSERV, +	CVMX_PKI_QPG_QOS_HIGIG, +}; + +enum cvmx_pki_wqe_vlan { CVMX_PKI_USE_FIRST_VLAN = 0, CVMX_PKI_USE_SECOND_VLAN }; + +/** + * Controls how the PKI statistics counters are handled + * The PKI_STAT*_X registers can be indexed either by port kind (pkind), or + * final style. (Does not apply to the PKI_STAT_INB* registers.) + *    0 = X represents the packet’s pkind + *    1 = X represents the low 6-bits of packet’s final style + */ +enum cvmx_pki_stats_mode { CVMX_PKI_STAT_MODE_PKIND, CVMX_PKI_STAT_MODE_STYLE }; + +enum cvmx_pki_fpa_wait { CVMX_PKI_DROP_PKT, CVMX_PKI_WAIT_PKT }; + +#define PKI_BELTYPE_E__NONE_M 0x0 +#define PKI_BELTYPE_E__MISC_M 0x1 +#define PKI_BELTYPE_E__IP4_M  0x2 +#define PKI_BELTYPE_E__IP6_M  0x3 +#define PKI_BELTYPE_E__TCP_M  0x4 +#define PKI_BELTYPE_E__UDP_M  0x5 +#define PKI_BELTYPE_E__SCTP_M 0x6 +#define PKI_BELTYPE_E__SNAP_M 0x7 + +/* PKI_BELTYPE_E_t */ +enum cvmx_pki_beltype { +	CVMX_PKI_BELTYPE_NONE = PKI_BELTYPE_E__NONE_M, +	CVMX_PKI_BELTYPE_MISC = PKI_BELTYPE_E__MISC_M, +	CVMX_PKI_BELTYPE_IP4 = PKI_BELTYPE_E__IP4_M, +	CVMX_PKI_BELTYPE_IP6 = PKI_BELTYPE_E__IP6_M, +	CVMX_PKI_BELTYPE_TCP = PKI_BELTYPE_E__TCP_M, +	CVMX_PKI_BELTYPE_UDP = PKI_BELTYPE_E__UDP_M, +	CVMX_PKI_BELTYPE_SCTP = PKI_BELTYPE_E__SCTP_M, +	CVMX_PKI_BELTYPE_SNAP = PKI_BELTYPE_E__SNAP_M, +	CVMX_PKI_BELTYPE_MAX = CVMX_PKI_BELTYPE_SNAP +}; + +struct cvmx_pki_frame_len { +	u16 maxlen; +	u16 minlen; +}; + +struct cvmx_pki_tag_fields { +	u64 layer_g_src : 1; +	u64 layer_f_src : 1; +	u64 layer_e_src : 1; +	u64 layer_d_src : 1; +	u64 layer_c_src : 1; +	u64 layer_b_src : 1; +	u64 layer_g_dst : 1; +	u64 layer_f_dst : 1; +	u64 layer_e_dst : 1; +	u64 layer_d_dst : 1; +	u64 layer_c_dst : 1; +	u64 layer_b_dst : 1; +	u64 input_port : 1; +	u64 mpls_label : 1; +	u64 first_vlan : 1; +	u64 second_vlan : 1; +	u64 ip_prot_nexthdr : 1; +	u64 tag_sync : 1; +	u64 tag_spi : 1; +	u64 tag_gtp : 1; +	u64 tag_vni : 1; +}; + +struct cvmx_pki_pkind_parse { +	u64 mpls_en : 1; +	u64 inst_hdr : 1; +	u64 lg_custom : 1; +	u64 fulc_en : 1; +	u64 dsa_en : 1; +	u64 hg2_en : 1; +	u64 hg_en : 1; +}; + +struct cvmx_pki_pool_config { +	int pool_num; +	cvmx_fpa3_pool_t pool; +	u64 buffer_size; +	u64 buffer_count; +}; + +struct cvmx_pki_qpg_config { +	int qpg_base; +	int port_add; +	int aura_num; +	int grp_ok; +	int grp_bad; +	int grptag_ok; +	int grptag_bad; +}; + +struct cvmx_pki_aura_config { +	int aura_num; +	int pool_num; +	cvmx_fpa3_pool_t pool; +	cvmx_fpa3_gaura_t aura; +	int buffer_count; +}; + +struct cvmx_pki_cluster_grp_config { +	int grp_num; +	u64 cluster_mask; /* Bit mask of cluster assigned to this cluster group */ +}; + +struct cvmx_pki_sso_grp_config { +	int group; +	int priority; +	int weight; +	int affinity; +	u64 core_mask; +	u8 core_mask_set; +}; + +/* This is per style structure for configuring port parameters, + * it is kind of of profile which can be assigned to any port. + * If multiple ports are assigned same style be aware that modifying + * that style will modify the respective parameters for all the ports + * which are using this style + */ +struct cvmx_pki_style_parm { +	bool ip6_udp_opt; +	bool lenerr_en; +	bool maxerr_en; +	bool minerr_en; +	u8 lenerr_eqpad; +	u8 minmax_sel; +	bool qpg_dis_grptag; +	bool fcs_strip; +	bool fcs_chk; +	bool rawdrp; +	bool force_drop; +	bool nodrop; +	bool qpg_dis_padd; +	bool qpg_dis_grp; +	bool qpg_dis_aura; +	u16 qpg_base; +	enum cvmx_pki_qpg_qos qpg_qos; +	u8 qpg_port_sh; +	u8 qpg_port_msb; +	u8 apad_nip; +	u8 wqe_vs; +	enum cvmx_sso_tag_type tag_type; +	bool pkt_lend; +	u8 wqe_hsz; +	u16 wqe_skip; +	u16 first_skip; +	u16 later_skip; +	enum cvmx_pki_cache_mode cache_mode; +	u8 dis_wq_dat; +	u64 mbuff_size; +	bool len_lg; +	bool len_lf; +	bool len_le; +	bool len_ld; +	bool len_lc; +	bool len_lb; +	bool csum_lg; +	bool csum_lf; +	bool csum_le; +	bool csum_ld; +	bool csum_lc; +	bool csum_lb; +}; + +/* This is per style structure for configuring port's tag configuration, + * it is kind of of profile which can be assigned to any port. + * If multiple ports are assigned same style be aware that modiying that style + * will modify the respective parameters for all the ports which are + * using this style */ +enum cvmx_pki_mtag_ptrsel { +	CVMX_PKI_MTAG_PTRSEL_SOP = 0, +	CVMX_PKI_MTAG_PTRSEL_LA = 8, +	CVMX_PKI_MTAG_PTRSEL_LB = 9, +	CVMX_PKI_MTAG_PTRSEL_LC = 10, +	CVMX_PKI_MTAG_PTRSEL_LD = 11, +	CVMX_PKI_MTAG_PTRSEL_LE = 12, +	CVMX_PKI_MTAG_PTRSEL_LF = 13, +	CVMX_PKI_MTAG_PTRSEL_LG = 14, +	CVMX_PKI_MTAG_PTRSEL_VL = 15, +}; + +struct cvmx_pki_mask_tag { +	bool enable; +	int base;   /* CVMX_PKI_MTAG_PTRSEL_XXX */ +	int offset; /* Offset from base. */ +	u64 val;    /* Bitmask: +		1 = enable, 0 = disabled for each byte in the 64-byte array.*/ +}; + +struct cvmx_pki_style_tag_cfg { +	struct cvmx_pki_tag_fields tag_fields; +	struct cvmx_pki_mask_tag mask_tag[4]; +}; + +struct cvmx_pki_style_config { +	struct cvmx_pki_style_parm parm_cfg; +	struct cvmx_pki_style_tag_cfg tag_cfg; +}; + +struct cvmx_pki_pkind_config { +	u8 cluster_grp; +	bool fcs_pres; +	struct cvmx_pki_pkind_parse parse_en; +	enum cvmx_pki_pkind_parse_mode initial_parse_mode; +	u8 fcs_skip; +	u8 inst_skip; +	int initial_style; +	bool custom_l2_hdr; +	u8 l2_scan_offset; +	u64 lg_scan_offset; +}; + +struct cvmx_pki_port_config { +	struct cvmx_pki_pkind_config pkind_cfg; +	struct cvmx_pki_style_config style_cfg; +}; + +struct cvmx_pki_global_parse { +	u64 virt_pen : 1; +	u64 clg_pen : 1; +	u64 cl2_pen : 1; +	u64 l4_pen : 1; +	u64 il3_pen : 1; +	u64 l3_pen : 1; +	u64 mpls_pen : 1; +	u64 fulc_pen : 1; +	u64 dsa_pen : 1; +	u64 hg_pen : 1; +}; + +struct cvmx_pki_tag_sec { +	u16 dst6; +	u16 src6; +	u16 dst; +	u16 src; +}; + +struct cvmx_pki_global_config { +	u64 cluster_mask[CVMX_PKI_NUM_CLUSTER_GROUP_MAX]; +	enum cvmx_pki_stats_mode stat_mode; +	enum cvmx_pki_fpa_wait fpa_wait; +	struct cvmx_pki_global_parse gbl_pen; +	struct cvmx_pki_tag_sec tag_secret; +	struct cvmx_pki_frame_len frm_len[CVMX_PKI_NUM_FRAME_CHECK]; +	enum cvmx_pki_beltype ltype_map[CVMX_PKI_NUM_BELTYPE]; +	int pki_enable; +}; + +#define CVMX_PKI_PCAM_TERM_E_NONE_M	 0x0 +#define CVMX_PKI_PCAM_TERM_E_L2_CUSTOM_M 0x2 +#define CVMX_PKI_PCAM_TERM_E_HIGIGD_M	 0x4 +#define CVMX_PKI_PCAM_TERM_E_HIGIG_M	 0x5 +#define CVMX_PKI_PCAM_TERM_E_SMACH_M	 0x8 +#define CVMX_PKI_PCAM_TERM_E_SMACL_M	 0x9 +#define CVMX_PKI_PCAM_TERM_E_DMACH_M	 0xA +#define CVMX_PKI_PCAM_TERM_E_DMACL_M	 0xB +#define CVMX_PKI_PCAM_TERM_E_GLORT_M	 0x12 +#define CVMX_PKI_PCAM_TERM_E_DSA_M	 0x13 +#define CVMX_PKI_PCAM_TERM_E_ETHTYPE0_M	 0x18 +#define CVMX_PKI_PCAM_TERM_E_ETHTYPE1_M	 0x19 +#define CVMX_PKI_PCAM_TERM_E_ETHTYPE2_M	 0x1A +#define CVMX_PKI_PCAM_TERM_E_ETHTYPE3_M	 0x1B +#define CVMX_PKI_PCAM_TERM_E_MPLS0_M	 0x1E +#define CVMX_PKI_PCAM_TERM_E_L3_SIPHH_M	 0x1F +#define CVMX_PKI_PCAM_TERM_E_L3_SIPMH_M	 0x20 +#define CVMX_PKI_PCAM_TERM_E_L3_SIPML_M	 0x21 +#define CVMX_PKI_PCAM_TERM_E_L3_SIPLL_M	 0x22 +#define CVMX_PKI_PCAM_TERM_E_L3_FLAGS_M	 0x23 +#define CVMX_PKI_PCAM_TERM_E_L3_DIPHH_M	 0x24 +#define CVMX_PKI_PCAM_TERM_E_L3_DIPMH_M	 0x25 +#define CVMX_PKI_PCAM_TERM_E_L3_DIPML_M	 0x26 +#define CVMX_PKI_PCAM_TERM_E_L3_DIPLL_M	 0x27 +#define CVMX_PKI_PCAM_TERM_E_LD_VNI_M	 0x28 +#define CVMX_PKI_PCAM_TERM_E_IL3_FLAGS_M 0x2B +#define CVMX_PKI_PCAM_TERM_E_LF_SPI_M	 0x2E +#define CVMX_PKI_PCAM_TERM_E_L4_SPORT_M	 0x2f +#define CVMX_PKI_PCAM_TERM_E_L4_PORT_M	 0x30 +#define CVMX_PKI_PCAM_TERM_E_LG_CUSTOM_M 0x39 + +enum cvmx_pki_term { +	CVMX_PKI_PCAM_TERM_NONE = CVMX_PKI_PCAM_TERM_E_NONE_M, +	CVMX_PKI_PCAM_TERM_L2_CUSTOM = CVMX_PKI_PCAM_TERM_E_L2_CUSTOM_M, +	CVMX_PKI_PCAM_TERM_HIGIGD = CVMX_PKI_PCAM_TERM_E_HIGIGD_M, +	CVMX_PKI_PCAM_TERM_HIGIG = CVMX_PKI_PCAM_TERM_E_HIGIG_M, +	CVMX_PKI_PCAM_TERM_SMACH = CVMX_PKI_PCAM_TERM_E_SMACH_M, +	CVMX_PKI_PCAM_TERM_SMACL = CVMX_PKI_PCAM_TERM_E_SMACL_M, +	CVMX_PKI_PCAM_TERM_DMACH = CVMX_PKI_PCAM_TERM_E_DMACH_M, +	CVMX_PKI_PCAM_TERM_DMACL = CVMX_PKI_PCAM_TERM_E_DMACL_M, +	CVMX_PKI_PCAM_TERM_GLORT = CVMX_PKI_PCAM_TERM_E_GLORT_M, +	CVMX_PKI_PCAM_TERM_DSA = CVMX_PKI_PCAM_TERM_E_DSA_M, +	CVMX_PKI_PCAM_TERM_ETHTYPE0 = CVMX_PKI_PCAM_TERM_E_ETHTYPE0_M, +	CVMX_PKI_PCAM_TERM_ETHTYPE1 = CVMX_PKI_PCAM_TERM_E_ETHTYPE1_M, +	CVMX_PKI_PCAM_TERM_ETHTYPE2 = CVMX_PKI_PCAM_TERM_E_ETHTYPE2_M, +	CVMX_PKI_PCAM_TERM_ETHTYPE3 = CVMX_PKI_PCAM_TERM_E_ETHTYPE3_M, +	CVMX_PKI_PCAM_TERM_MPLS0 = CVMX_PKI_PCAM_TERM_E_MPLS0_M, +	CVMX_PKI_PCAM_TERM_L3_SIPHH = CVMX_PKI_PCAM_TERM_E_L3_SIPHH_M, +	CVMX_PKI_PCAM_TERM_L3_SIPMH = CVMX_PKI_PCAM_TERM_E_L3_SIPMH_M, +	CVMX_PKI_PCAM_TERM_L3_SIPML = CVMX_PKI_PCAM_TERM_E_L3_SIPML_M, +	CVMX_PKI_PCAM_TERM_L3_SIPLL = CVMX_PKI_PCAM_TERM_E_L3_SIPLL_M, +	CVMX_PKI_PCAM_TERM_L3_FLAGS = CVMX_PKI_PCAM_TERM_E_L3_FLAGS_M, +	CVMX_PKI_PCAM_TERM_L3_DIPHH = CVMX_PKI_PCAM_TERM_E_L3_DIPHH_M, +	CVMX_PKI_PCAM_TERM_L3_DIPMH = CVMX_PKI_PCAM_TERM_E_L3_DIPMH_M, +	CVMX_PKI_PCAM_TERM_L3_DIPML = CVMX_PKI_PCAM_TERM_E_L3_DIPML_M, +	CVMX_PKI_PCAM_TERM_L3_DIPLL = CVMX_PKI_PCAM_TERM_E_L3_DIPLL_M, +	CVMX_PKI_PCAM_TERM_LD_VNI = CVMX_PKI_PCAM_TERM_E_LD_VNI_M, +	CVMX_PKI_PCAM_TERM_IL3_FLAGS = CVMX_PKI_PCAM_TERM_E_IL3_FLAGS_M, +	CVMX_PKI_PCAM_TERM_LF_SPI = CVMX_PKI_PCAM_TERM_E_LF_SPI_M, +	CVMX_PKI_PCAM_TERM_L4_PORT = CVMX_PKI_PCAM_TERM_E_L4_PORT_M, +	CVMX_PKI_PCAM_TERM_L4_SPORT = CVMX_PKI_PCAM_TERM_E_L4_SPORT_M, +	CVMX_PKI_PCAM_TERM_LG_CUSTOM = CVMX_PKI_PCAM_TERM_E_LG_CUSTOM_M +}; + +#define CVMX_PKI_DMACH_SHIFT	  32 +#define CVMX_PKI_DMACH_MASK	  cvmx_build_mask(16) +#define CVMX_PKI_DMACL_MASK	  CVMX_PKI_DATA_MASK_32 +#define CVMX_PKI_DATA_MASK_32	  cvmx_build_mask(32) +#define CVMX_PKI_DATA_MASK_16	  cvmx_build_mask(16) +#define CVMX_PKI_DMAC_MATCH_EXACT cvmx_build_mask(48) + +struct cvmx_pki_pcam_input { +	u64 style; +	u64 style_mask; /* bits: 1-match, 0-dont care */ +	enum cvmx_pki_term field; +	u32 field_mask; /* bits: 1-match, 0-dont care */ +	u64 data; +	u64 data_mask; /* bits: 1-match, 0-dont care */ +}; + +struct cvmx_pki_pcam_action { +	enum cvmx_pki_parse_mode_chg parse_mode_chg; +	enum cvmx_pki_layer_type layer_type_set; +	int style_add; +	int parse_flag_set; +	int pointer_advance; +}; + +struct cvmx_pki_pcam_config { +	int in_use; +	int entry_num; +	u64 cluster_mask; +	struct cvmx_pki_pcam_input pcam_input; +	struct cvmx_pki_pcam_action pcam_action; +}; + +/** + * Status statistics for a port + */ +struct cvmx_pki_port_stats { +	u64 dropped_octets; +	u64 dropped_packets; +	u64 pci_raw_packets; +	u64 octets; +	u64 packets; +	u64 multicast_packets; +	u64 broadcast_packets; +	u64 len_64_packets; +	u64 len_65_127_packets; +	u64 len_128_255_packets; +	u64 len_256_511_packets; +	u64 len_512_1023_packets; +	u64 len_1024_1518_packets; +	u64 len_1519_max_packets; +	u64 fcs_align_err_packets; +	u64 runt_packets; +	u64 runt_crc_packets; +	u64 oversize_packets; +	u64 oversize_crc_packets; +	u64 inb_packets; +	u64 inb_octets; +	u64 inb_errors; +	u64 mcast_l2_red_packets; +	u64 bcast_l2_red_packets; +	u64 mcast_l3_red_packets; +	u64 bcast_l3_red_packets; +}; + +/** + * PKI Packet Instruction Header Structure (PKI_INST_HDR_S) + */ +typedef union { +	u64 u64; +	struct { +		u64 w : 1;    /* INST_HDR size: 0 = 2 bytes, 1 = 4 or 8 bytes */ +		u64 raw : 1;  /* RAW packet indicator in WQE[RAW]: 1 = enable */ +		u64 utag : 1; /* Use INST_HDR[TAG] to compute WQE[TAG]: 1 = enable */ +		u64 uqpg : 1; /* Use INST_HDR[QPG] to compute QPG: 1 = enable */ +		u64 rsvd1 : 1; +		u64 pm : 3; /* Packet parsing mode. Legal values = 0x0..0x7 */ +		u64 sl : 8; /* Number of bytes in INST_HDR. */ +		/* The following fields are not present, if INST_HDR[W] = 0: */ +		u64 utt : 1; /* Use INST_HDR[TT] to compute WQE[TT]: 1 = enable */ +		u64 tt : 2;  /* INST_HDR[TT] => WQE[TT], if INST_HDR[UTT] = 1 */ +		u64 rsvd2 : 2; +		u64 qpg : 11; /* INST_HDR[QPG] => QPG, if INST_HDR[UQPG] = 1 */ +		u64 tag : 32; /* INST_HDR[TAG] => WQE[TAG], if INST_HDR[UTAG] = 1 */ +	} s; +} cvmx_pki_inst_hdr_t; + +/** + * This function assignes the clusters to a group, later pkind can be + * configured to use that group depending on number of clusters pkind + * would use. A given cluster can only be enabled in a single cluster group. + * Number of clusters assign to that group determines how many engine can work + * in parallel to process the packet. Eack cluster can process x MPPS. + * + * @param node	Node + * @param cluster_group Group to attach clusters to. + * @param cluster_mask The mask of clusters which needs to be assigned to the group. + */ +static inline int cvmx_pki_attach_cluster_to_group(int node, u64 cluster_group, u64 cluster_mask) +{ +	cvmx_pki_icgx_cfg_t pki_cl_grp; + +	if (cluster_group >= CVMX_PKI_NUM_CLUSTER_GROUP) { +		debug("ERROR: config cluster group %d", (int)cluster_group); +		return -1; +	} +	pki_cl_grp.u64 = cvmx_read_csr_node(node, CVMX_PKI_ICGX_CFG(cluster_group)); +	pki_cl_grp.s.clusters = cluster_mask; +	cvmx_write_csr_node(node, CVMX_PKI_ICGX_CFG(cluster_group), pki_cl_grp.u64); +	return 0; +} + +static inline void cvmx_pki_write_global_parse(int node, struct cvmx_pki_global_parse gbl_pen) +{ +	cvmx_pki_gbl_pen_t gbl_pen_reg; + +	gbl_pen_reg.u64 = cvmx_read_csr_node(node, CVMX_PKI_GBL_PEN); +	gbl_pen_reg.s.virt_pen = gbl_pen.virt_pen; +	gbl_pen_reg.s.clg_pen = gbl_pen.clg_pen; +	gbl_pen_reg.s.cl2_pen = gbl_pen.cl2_pen; +	gbl_pen_reg.s.l4_pen = gbl_pen.l4_pen; +	gbl_pen_reg.s.il3_pen = gbl_pen.il3_pen; +	gbl_pen_reg.s.l3_pen = gbl_pen.l3_pen; +	gbl_pen_reg.s.mpls_pen = gbl_pen.mpls_pen; +	gbl_pen_reg.s.fulc_pen = gbl_pen.fulc_pen; +	gbl_pen_reg.s.dsa_pen = gbl_pen.dsa_pen; +	gbl_pen_reg.s.hg_pen = gbl_pen.hg_pen; +	cvmx_write_csr_node(node, CVMX_PKI_GBL_PEN, gbl_pen_reg.u64); +} + +static inline void cvmx_pki_write_tag_secret(int node, struct cvmx_pki_tag_sec tag_secret) +{ +	cvmx_pki_tag_secret_t tag_secret_reg; + +	tag_secret_reg.u64 = cvmx_read_csr_node(node, CVMX_PKI_TAG_SECRET); +	tag_secret_reg.s.dst6 = tag_secret.dst6; +	tag_secret_reg.s.src6 = tag_secret.src6; +	tag_secret_reg.s.dst = tag_secret.dst; +	tag_secret_reg.s.src = tag_secret.src; +	cvmx_write_csr_node(node, CVMX_PKI_TAG_SECRET, tag_secret_reg.u64); +} + +static inline void cvmx_pki_write_ltype_map(int node, enum cvmx_pki_layer_type layer, +					    enum cvmx_pki_beltype backend) +{ +	cvmx_pki_ltypex_map_t ltype_map; + +	if (layer > CVMX_PKI_LTYPE_E_MAX || backend > CVMX_PKI_BELTYPE_MAX) { +		debug("ERROR: invalid ltype beltype mapping\n"); +		return; +	} +	ltype_map.u64 = cvmx_read_csr_node(node, CVMX_PKI_LTYPEX_MAP(layer)); +	ltype_map.s.beltype = backend; +	cvmx_write_csr_node(node, CVMX_PKI_LTYPEX_MAP(layer), ltype_map.u64); +} + +/** + * This function enables the cluster group to start parsing. + * + * @param node    Node number. + * @param cl_grp  Cluster group to enable parsing. + */ +static inline int cvmx_pki_parse_enable(int node, unsigned int cl_grp) +{ +	cvmx_pki_icgx_cfg_t pki_cl_grp; + +	if (cl_grp >= CVMX_PKI_NUM_CLUSTER_GROUP) { +		debug("ERROR: pki parse en group %d", (int)cl_grp); +		return -1; +	} +	pki_cl_grp.u64 = cvmx_read_csr_node(node, CVMX_PKI_ICGX_CFG(cl_grp)); +	pki_cl_grp.s.pena = 1; +	cvmx_write_csr_node(node, CVMX_PKI_ICGX_CFG(cl_grp), pki_cl_grp.u64); +	return 0; +} + +/** + * This function enables the PKI to send bpid level backpressure to CN78XX inputs. + * + * @param node Node number. + */ +static inline void cvmx_pki_enable_backpressure(int node) +{ +	cvmx_pki_buf_ctl_t pki_buf_ctl; + +	pki_buf_ctl.u64 = cvmx_read_csr_node(node, CVMX_PKI_BUF_CTL); +	pki_buf_ctl.s.pbp_en = 1; +	cvmx_write_csr_node(node, CVMX_PKI_BUF_CTL, pki_buf_ctl.u64); +} + +/** + * Clear the statistics counters for a port. + * + * @param node Node number. + * @param port Port number (ipd_port) to get statistics for. + *    Make sure PKI_STATS_CTL:mode is set to 0 for collecting per port/pkind stats. + */ +void cvmx_pki_clear_port_stats(int node, u64 port); + +/** + * Get the status counters for index from PKI. + * + * @param node	  Node number. + * @param index   PKIND number, if PKI_STATS_CTL:mode = 0 or + *     style(flow) number, if PKI_STATS_CTL:mode = 1 + * @param status  Where to put the results. + */ +void cvmx_pki_get_stats(int node, int index, struct cvmx_pki_port_stats *status); + +/** + * Get the statistics counters for a port. + * + * @param node	 Node number + * @param port   Port number (ipd_port) to get statistics for. + *    Make sure PKI_STATS_CTL:mode is set to 0 for collecting per port/pkind stats. + * @param status Where to put the results. + */ +static inline void cvmx_pki_get_port_stats(int node, u64 port, struct cvmx_pki_port_stats *status) +{ +	int xipd = cvmx_helper_node_to_ipd_port(node, port); +	int xiface = cvmx_helper_get_interface_num(xipd); +	int index = cvmx_helper_get_interface_index_num(port); +	int pknd = cvmx_helper_get_pknd(xiface, index); + +	cvmx_pki_get_stats(node, pknd, status); +} + +/** + * Get the statistics counters for a flow represented by style in PKI. + * + * @param node Node number. + * @param style_num Style number to get statistics for. + *    Make sure PKI_STATS_CTL:mode is set to 1 for collecting per style/flow stats. + * @param status Where to put the results. + */ +static inline void cvmx_pki_get_flow_stats(int node, u64 style_num, +					   struct cvmx_pki_port_stats *status) +{ +	cvmx_pki_get_stats(node, style_num, status); +} + +/** + * Show integrated PKI configuration. + * + * @param node	   node number + */ +int cvmx_pki_config_dump(unsigned int node); + +/** + * Show integrated PKI statistics. + * + * @param node	   node number + */ +int cvmx_pki_stats_dump(unsigned int node); + +/** + * Clear PKI statistics. + * + * @param node	   node number + */ +void cvmx_pki_stats_clear(unsigned int node); + +/** + * This function enables PKI. + * + * @param node	 node to enable pki in. + */ +void cvmx_pki_enable(int node); + +/** + * This function disables PKI. + * + * @param node	node to disable pki in. + */ +void cvmx_pki_disable(int node); + +/** + * This function soft resets PKI. + * + * @param node	node to enable pki in. + */ +void cvmx_pki_reset(int node); + +/** + * This function sets the clusters in PKI. + * + * @param node	node to set clusters in. + */ +int cvmx_pki_setup_clusters(int node); + +/** + * This function reads global configuration of PKI block. + * + * @param node    Node number. + * @param gbl_cfg Pointer to struct to read global configuration + */ +void cvmx_pki_read_global_config(int node, struct cvmx_pki_global_config *gbl_cfg); + +/** + * This function writes global configuration of PKI into hw. + * + * @param node    Node number. + * @param gbl_cfg Pointer to struct to global configuration + */ +void cvmx_pki_write_global_config(int node, struct cvmx_pki_global_config *gbl_cfg); + +/** + * This function reads per pkind parameters in hardware which defines how + * the incoming packet is processed. + * + * @param node   Node number. + * @param pkind  PKI supports a large number of incoming interfaces and packets + *     arriving on different interfaces or channels may want to be processed + *     differently. PKI uses the pkind to determine how the incoming packet + *     is processed. + * @param pkind_cfg	Pointer to struct conatining pkind configuration read + *     from hardware. + */ +int cvmx_pki_read_pkind_config(int node, int pkind, struct cvmx_pki_pkind_config *pkind_cfg); + +/** + * This function writes per pkind parameters in hardware which defines how + * the incoming packet is processed. + * + * @param node   Node number. + * @param pkind  PKI supports a large number of incoming interfaces and packets + *     arriving on different interfaces or channels may want to be processed + *     differently. PKI uses the pkind to determine how the incoming packet + *     is processed. + * @param pkind_cfg	Pointer to struct conatining pkind configuration need + *     to be written in hardware. + */ +int cvmx_pki_write_pkind_config(int node, int pkind, struct cvmx_pki_pkind_config *pkind_cfg); + +/** + * This function reads parameters associated with tag configuration in hardware. + * + * @param node	 Node number. + * @param style  Style to configure tag for. + * @param cluster_mask  Mask of clusters to configure the style for. + * @param tag_cfg  Pointer to tag configuration struct. + */ +void cvmx_pki_read_tag_config(int node, int style, u64 cluster_mask, +			      struct cvmx_pki_style_tag_cfg *tag_cfg); + +/** + * This function writes/configures parameters associated with tag + * configuration in hardware. + * + * @param node  Node number. + * @param style  Style to configure tag for. + * @param cluster_mask  Mask of clusters to configure the style for. + * @param tag_cfg  Pointer to taf configuration struct. + */ +void cvmx_pki_write_tag_config(int node, int style, u64 cluster_mask, +			       struct cvmx_pki_style_tag_cfg *tag_cfg); + +/** + * This function reads parameters associated with style in hardware. + * + * @param node	Node number. + * @param style  Style to read from. + * @param cluster_mask  Mask of clusters style belongs to. + * @param style_cfg  Pointer to style config struct. + */ +void cvmx_pki_read_style_config(int node, int style, u64 cluster_mask, +				struct cvmx_pki_style_config *style_cfg); + +/** + * This function writes/configures parameters associated with style in hardware. + * + * @param node  Node number. + * @param style  Style to configure. + * @param cluster_mask  Mask of clusters to configure the style for. + * @param style_cfg  Pointer to style config struct. + */ +void cvmx_pki_write_style_config(int node, u64 style, u64 cluster_mask, +				 struct cvmx_pki_style_config *style_cfg); +/** + * This function reads qpg entry at specified offset from qpg table + * + * @param node  Node number. + * @param offset  Offset in qpg table to read from. + * @param qpg_cfg  Pointer to structure containing qpg values + */ +int cvmx_pki_read_qpg_entry(int node, int offset, struct cvmx_pki_qpg_config *qpg_cfg); + +/** + * This function writes qpg entry at specified offset in qpg table + * + * @param node  Node number. + * @param offset  Offset in qpg table to write to. + * @param qpg_cfg  Pointer to stricture containing qpg values. + */ +void cvmx_pki_write_qpg_entry(int node, int offset, struct cvmx_pki_qpg_config *qpg_cfg); + +/** + * This function writes pcam entry at given offset in pcam table in hardware + * + * @param node  Node number. + * @param index	 Offset in pcam table. + * @param cluster_mask  Mask of clusters in which to write pcam entry. + * @param input  Input keys to pcam match passed as struct. + * @param action  PCAM match action passed as struct + */ +int cvmx_pki_pcam_write_entry(int node, int index, u64 cluster_mask, +			      struct cvmx_pki_pcam_input input, struct cvmx_pki_pcam_action action); +/** + * Configures the channel which will receive backpressure from the specified bpid. + * Each channel listens for backpressure on a specific bpid. + * Each bpid can backpressure multiple channels. + * @param node  Node number. + * @param bpid  BPID from which channel will receive backpressure. + * @param channel  Channel number to receive backpressue. + */ +int cvmx_pki_write_channel_bpid(int node, int channel, int bpid); + +/** + * Configures the bpid on which, specified channel will + * assert backpressure. + * Each bpid receives backpressure from auras. + * Multiple auras can backpressure single bpid. + * @param node  Node number. + * @param aura  Number which will assert backpressure on that bpid. + * @param bpid  To assert backpressure on. + */ +int cvmx_pki_write_aura_bpid(int node, int aura, int bpid); + +/** + * Enables/Disabled QoS (RED Drop, Tail Drop & backpressure) for the* PKI aura. + * + * @param node  Node number + * @param aura  To enable/disable QoS on. + * @param ena_red  Enable/Disable RED drop between pass and drop level + *    1-enable 0-disable + * @param ena_drop  Enable/disable tail drop when max drop level exceeds + *    1-enable 0-disable + * @param ena_bp  Enable/Disable asserting backpressure on bpid when + *    max DROP level exceeds. + *    1-enable 0-disable + */ +int cvmx_pki_enable_aura_qos(int node, int aura, bool ena_red, bool ena_drop, bool ena_bp); + +/** + * This function gives the initial style used by that pkind. + * + * @param node  Node number. + * @param pkind  PKIND number. + */ +int cvmx_pki_get_pkind_style(int node, int pkind); + +/** + * This function sets the wqe buffer mode. First packet data buffer can reside + * either in same buffer as wqe OR it can go in separate buffer. If used the later mode, + * make sure software allocate enough buffers to now have wqe separate from packet data. + * + * @param node  Node number. + * @param style  Style to configure. + * @param pkt_outside_wqe + *    0 = The packet link pointer will be at word [FIRST_SKIP] immediately + *    followed by packet data, in the same buffer as the work queue entry. + *    1 = The packet link pointer will be at word [FIRST_SKIP] in a new + *    buffer separate from the work queue entry. Words following the + *    WQE in the same cache line will be zeroed, other lines in the + *    buffer will not be modified and will retain stale data (from the + *    buffer’s previous use). This setting may decrease the peak PKI + *    performance by up to half on small packets. + */ +void cvmx_pki_set_wqe_mode(int node, u64 style, bool pkt_outside_wqe); + +/** + * This function sets the Packet mode of all ports and styles to little-endian. + * It Changes write operations of packet data to L2C to + * be in little-endian. Does not change the WQE header format, which is + * properly endian neutral. + * + * @param node  Node number. + * @param style  Style to configure. + */ +void cvmx_pki_set_little_endian(int node, u64 style); + +/** + * Enables/Disables L2 length error check and max & min frame length checks. + * + * @param node  Node number. + * @param pknd  PKIND to disable error for. + * @param l2len_err	 L2 length error check enable. + * @param maxframe_err	Max frame error check enable. + * @param minframe_err	Min frame error check enable. + *    1 -- Enabel err checks + *    0 -- Disable error checks + */ +void cvmx_pki_endis_l2_errs(int node, int pknd, bool l2len_err, bool maxframe_err, +			    bool minframe_err); + +/** + * Enables/Disables fcs check and fcs stripping on the pkind. + * + * @param node  Node number. + * @param pknd  PKIND to apply settings on. + * @param fcs_chk  Enable/disable fcs check. + *    1 -- enable fcs error check. + *    0 -- disable fcs error check. + * @param fcs_strip	 Strip L2 FCS bytes from packet, decrease WQE[LEN] by 4 bytes + *    1 -- strip L2 FCS. + *    0 -- Do not strip L2 FCS. + */ +void cvmx_pki_endis_fcs_check(int node, int pknd, bool fcs_chk, bool fcs_strip); + +/** + * This function shows the qpg table entries, read directly from hardware. + * + * @param node  Node number. + * @param num_entry  Number of entries to print. + */ +void cvmx_pki_show_qpg_entries(int node, u16 num_entry); + +/** + * This function shows the pcam table in raw format read directly from hardware. + * + * @param node  Node number. + */ +void cvmx_pki_show_pcam_entries(int node); + +/** + * This function shows the valid entries in readable format, + * read directly from hardware. + * + * @param node  Node number. + */ +void cvmx_pki_show_valid_pcam_entries(int node); + +/** + * This function shows the pkind attributes in readable format, + * read directly from hardware. + * @param node  Node number. + * @param pkind  PKIND number to print. + */ +void cvmx_pki_show_pkind_attributes(int node, int pkind); + +/** + * @INTERNAL + * This function is called by cvmx_helper_shutdown() to extract all FPA buffers + * out of the PKI. After this function completes, all FPA buffers that were + * prefetched by PKI will be in the appropriate FPA pool. + * This functions does not reset the PKI. + * WARNING: It is very important that PKI be reset soon after a call to this function. + * + * @param node  Node number. + */ +void __cvmx_pki_free_ptr(int node); + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pko-internal-ports-range.h b/arch/mips/mach-octeon/include/mach/cvmx-pko-internal-ports-range.h new file mode 100644 index 00000000000..1fb49b3fb6d --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pko-internal-ports-range.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + */ + +#ifndef __CVMX_INTERNAL_PORTS_RANGE__ +#define __CVMX_INTERNAL_PORTS_RANGE__ + +/* + * Allocated a block of internal ports for the specified interface/port + * + * @param  interface  the interface for which the internal ports are requested + * @param  port       the index of the port within in the interface for which the internal ports + *                    are requested. + * @param  count      the number of internal ports requested + * + * @return  0 on success + *         -1 on failure + */ +int cvmx_pko_internal_ports_alloc(int interface, int port, u64 count); + +/* + * Free the internal ports associated with the specified interface/port + * + * @param  interface  the interface for which the internal ports are requested + * @param  port       the index of the port within in the interface for which the internal ports + *                    are requested. + * + * @return  0 on success + *         -1 on failure + */ +int cvmx_pko_internal_ports_free(int interface, int port); + +/* + * Frees up all the allocated internal ports. + */ +void cvmx_pko_internal_ports_range_free_all(void); + +void cvmx_pko_internal_ports_range_show(void); + +int __cvmx_pko_internal_ports_range_init(void); + +#endif diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pko3-queue.h b/arch/mips/mach-octeon/include/mach/cvmx-pko3-queue.h new file mode 100644 index 00000000000..5f839890495 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pko3-queue.h @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + */ + +#ifndef __CVMX_PKO3_QUEUE_H__ +#define __CVMX_PKO3_QUEUE_H__ + +/** + * @INTERNAL + * + * Find or allocate global port/dq map table + * which is a named table, contains entries for + * all possible OCI nodes. + * + * The table global pointer is stored in core-local variable + * so that every core will call this function once, on first use. + */ +int __cvmx_pko3_dq_table_setup(void); + +/* + * Get the base Descriptor Queue number for an IPD port on the local node + */ +int cvmx_pko3_get_queue_base(int ipd_port); + +/* + * Get the number of Descriptor Queues assigned for an IPD port + */ +int cvmx_pko3_get_queue_num(int ipd_port); + +/** + * Get L1/Port Queue number assigned to interface port. + * + * @param xiface is interface number. + * @param index is port index. + */ +int cvmx_pko3_get_port_queue(int xiface, int index); + +/* + * Configure L3 through L5 Scheduler Queues and Descriptor Queues + * + * The Scheduler Queues in Levels 3 to 5 and Descriptor Queues are + * configured one-to-one or many-to-one to a single parent Scheduler + * Queues. The level of the parent SQ is specified in an argument, + * as well as the number of children to attach to the specific parent. + * The children can have fair round-robin or priority-based scheduling + * when multiple children are assigned a single parent. + * + * @param node is the OCI node location for the queues to be configured + * @param parent_level is the level of the parent queue, 2 to 5. + * @param parent_queue is the number of the parent Scheduler Queue + * @param child_base is the number of the first child SQ or DQ to assign to + * @param parent + * @param child_count is the number of consecutive children to assign + * @param stat_prio_count is the priority setting for the children L2 SQs + * + * If <stat_prio_count> is -1, the Ln children will have equal Round-Robin + * relationship with eachother. If <stat_prio_count> is 0, all Ln children + * will be arranged in Weighted-Round-Robin, with the first having the most + * precedence. If <stat_prio_count> is between 1 and 8, it indicates how + * many children will have static priority settings (with the first having + * the most precedence), with the remaining Ln children having WRR scheduling. + * + * @returns 0 on success, -1 on failure. + * + * Note: this function supports the configuration of node-local unit. + */ +int cvmx_pko3_sq_config_children(unsigned int node, unsigned int parent_level, +				 unsigned int parent_queue, unsigned int child_base, +				 unsigned int child_count, int stat_prio_count); + +/* + * @INTERNAL + * Register a range of Descriptor Queues wth an interface port + * + * This function poulates the DQ-to-IPD translation table + * used by the application to retrieve the DQ range (typically ordered + * by priority) for a given IPD-port, which is either a physical port, + * or a channel on a channelized interface (i.e. ILK). + * + * @param xiface is the physical interface number + * @param index is either a physical port on an interface + * @param or a channel of an ILK interface + * @param dq_base is the first Descriptor Queue number in a consecutive range + * @param dq_count is the number of consecutive Descriptor Queues leading + * @param the same channel or port. + * + * Only a consecurive range of Descriptor Queues can be associated with any + * given channel/port, and usually they are ordered from most to least + * in terms of scheduling priority. + * + * Note: thus function only populates the node-local translation table. + * + * @returns 0 on success, -1 on failure. + */ +int __cvmx_pko3_ipd_dq_register(int xiface, int index, unsigned int dq_base, unsigned int dq_count); + +/** + * @INTERNAL + * + * Unregister DQs associated with CHAN_E (IPD port) + */ +int __cvmx_pko3_ipd_dq_unregister(int xiface, int index); + +/* + * Map channel number in PKO + * + * @param node is to specify the node to which this configuration is applied. + * @param pq_num specifies the Port Queue (i.e. L1) queue number. + * @param l2_l3_q_num  specifies L2/L3 queue number. + * @param channel specifies the channel number to map to the queue. + * + * The channel assignment applies to L2 or L3 Shaper Queues depending + * on the setting of channel credit level. + * + * @return returns none. + */ +void cvmx_pko3_map_channel(unsigned int node, unsigned int pq_num, unsigned int l2_l3_q_num, +			   u16 channel); + +int cvmx_pko3_pq_config(unsigned int node, unsigned int mac_num, unsigned int pq_num); + +int cvmx_pko3_port_cir_set(unsigned int node, unsigned int pq_num, unsigned long rate_kbips, +			   unsigned int burst_bytes, int adj_bytes); +int cvmx_pko3_dq_cir_set(unsigned int node, unsigned int pq_num, unsigned long rate_kbips, +			 unsigned int burst_bytes); +int cvmx_pko3_dq_pir_set(unsigned int node, unsigned int pq_num, unsigned long rate_kbips, +			 unsigned int burst_bytes); +typedef enum { +	CVMX_PKO3_SHAPE_RED_STALL, +	CVMX_PKO3_SHAPE_RED_DISCARD, +	CVMX_PKO3_SHAPE_RED_PASS +} red_action_t; + +void cvmx_pko3_dq_red(unsigned int node, unsigned int dq_num, red_action_t red_act, +		      int8_t len_adjust); + +/** + * Macros to deal with short floating point numbers, + * where unsigned exponent, and an unsigned normalized + * mantissa are represented each with a defined field width. + * + */ +#define CVMX_SHOFT_MANT_BITS 8 +#define CVMX_SHOFT_EXP_BITS  4 + +/** + * Convert short-float to an unsigned integer + * Note that it will lose precision. + */ +#define CVMX_SHOFT_TO_U64(m, e)                                                                    \ +	((((1ull << CVMX_SHOFT_MANT_BITS) | (m)) << (e)) >> CVMX_SHOFT_MANT_BITS) + +/** + * Convert to short-float from an unsigned integer + */ +#define CVMX_SHOFT_FROM_U64(ui, m, e)                                                              \ +	do {                                                                                       \ +		unsigned long long u;                                                              \ +		unsigned int k;                                                                    \ +		k = (1ull << (CVMX_SHOFT_MANT_BITS + 1)) - 1;                                      \ +		(e) = 0;                                                                           \ +		u = (ui) << CVMX_SHOFT_MANT_BITS;                                                  \ +		while ((u) > k) {                                                                  \ +			u >>= 1;                                                                   \ +			(e)++;                                                                     \ +		}                                                                                  \ +		(m) = u & (k >> 1);                                                                \ +	} while (0); + +#define CVMX_SHOFT_MAX()                                                                           \ +	CVMX_SHOFT_TO_U64((1 << CVMX_SHOFT_MANT_BITS) - 1, (1 << CVMX_SHOFT_EXP_BITS) - 1) +#define CVMX_SHOFT_MIN() CVMX_SHOFT_TO_U64(0, 0) + +#endif /* __CVMX_PKO3_QUEUE_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-pow.h b/arch/mips/mach-octeon/include/mach/cvmx-pow.h new file mode 100644 index 00000000000..0680ca258f1 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-pow.h @@ -0,0 +1,2991 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * Interface to the hardware Scheduling unit. + * + * New, starting with SDK 1.7.0, cvmx-pow supports a number of + * extended consistency checks. The define + * CVMX_ENABLE_POW_CHECKS controls the runtime insertion of POW + * internal state checks to find common programming errors. If + * CVMX_ENABLE_POW_CHECKS is not defined, checks are by default + * enabled. For example, cvmx-pow will check for the following + * program errors or POW state inconsistency. + * - Requesting a POW operation with an active tag switch in + *   progress. + * - Waiting for a tag switch to complete for an excessively + *   long period. This is normally a sign of an error in locking + *   causing deadlock. + * - Illegal tag switches from NULL_NULL. + * - Illegal tag switches from NULL. + * - Illegal deschedule request. + * - WQE pointer not matching the one attached to the core by + *   the POW. + */ + +#ifndef __CVMX_POW_H__ +#define __CVMX_POW_H__ + +#include "cvmx-wqe.h" +#include "cvmx-pow-defs.h" +#include "cvmx-sso-defs.h" +#include "cvmx-address.h" +#include "cvmx-coremask.h" + +/* Default to having all POW constancy checks turned on */ +#ifndef CVMX_ENABLE_POW_CHECKS +#define CVMX_ENABLE_POW_CHECKS 1 +#endif + +/* + * Special type for CN78XX style SSO groups (0..255), + * for distinction from legacy-style groups (0..15) + */ +typedef union { +	u8 xgrp; +	/* Fields that map XGRP for backwards compatibility */ +	struct __attribute__((__packed__)) { +		u8 group : 5; +		u8 qus : 3; +	}; +} cvmx_xgrp_t; + +/* + * Softwsare-only structure to convey a return value + * containing multiple information fields about an work queue entry + */ +typedef struct { +	u32 tag; +	u16 index; +	u8 grp; /* Legacy group # (0..15) */ +	u8 tag_type; +} cvmx_pow_tag_info_t; + +/** + * Wait flag values for pow functions. + */ +typedef enum { +	CVMX_POW_WAIT = 1, +	CVMX_POW_NO_WAIT = 0, +} cvmx_pow_wait_t; + +/** + *  POW tag operations.  These are used in the data stored to the POW. + */ +typedef enum { +	CVMX_POW_TAG_OP_SWTAG = 0L, +	CVMX_POW_TAG_OP_SWTAG_FULL = 1L, +	CVMX_POW_TAG_OP_SWTAG_DESCH = 2L, +	CVMX_POW_TAG_OP_DESCH = 3L, +	CVMX_POW_TAG_OP_ADDWQ = 4L, +	CVMX_POW_TAG_OP_UPDATE_WQP_GRP = 5L, +	CVMX_POW_TAG_OP_SET_NSCHED = 6L, +	CVMX_POW_TAG_OP_CLR_NSCHED = 7L, +	CVMX_POW_TAG_OP_NOP = 15L +} cvmx_pow_tag_op_t; + +/** + * This structure defines the store data on a store to POW + */ +typedef union { +	u64 u64; +	struct { +		u64 no_sched : 1; +		u64 unused : 2; +		u64 index : 13; +		cvmx_pow_tag_op_t op : 4; +		u64 unused2 : 2; +		u64 qos : 3; +		u64 grp : 4; +		cvmx_pow_tag_type_t type : 3; +		u64 tag : 32; +	} s_cn38xx; +	struct { +		u64 no_sched : 1; +		cvmx_pow_tag_op_t op : 4; +		u64 unused1 : 4; +		u64 index : 11; +		u64 unused2 : 1; +		u64 grp : 6; +		u64 unused3 : 3; +		cvmx_pow_tag_type_t type : 2; +		u64 tag : 32; +	} s_cn68xx_clr; +	struct { +		u64 no_sched : 1; +		cvmx_pow_tag_op_t op : 4; +		u64 unused1 : 12; +		u64 qos : 3; +		u64 unused2 : 1; +		u64 grp : 6; +		u64 unused3 : 3; +		cvmx_pow_tag_type_t type : 2; +		u64 tag : 32; +	} s_cn68xx_add; +	struct { +		u64 no_sched : 1; +		cvmx_pow_tag_op_t op : 4; +		u64 unused1 : 16; +		u64 grp : 6; +		u64 unused3 : 3; +		cvmx_pow_tag_type_t type : 2; +		u64 tag : 32; +	} s_cn68xx_other; +	struct { +		u64 rsvd_62_63 : 2; +		u64 grp : 10; +		cvmx_pow_tag_type_t type : 2; +		u64 no_sched : 1; +		u64 rsvd_48 : 1; +		cvmx_pow_tag_op_t op : 4; +		u64 rsvd_42_43 : 2; +		u64 wqp : 42; +	} s_cn78xx_other; + +} cvmx_pow_tag_req_t; + +union cvmx_pow_tag_req_addr { +	u64 u64; +	struct { +		u64 mem_region : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 addr : 40; +	} s; +	struct { +		u64 mem_region : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 node : 4; +		u64 tag : 32; +		u64 reserved_0_3 : 4; +	} s_cn78xx; +}; + +/** + * This structure describes the address to load stuff from POW + */ +typedef union { +	u64 u64; +	/** +	 * Address for new work request loads (did<2:0> == 0) +	 */ +	struct { +		u64 mem_region : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 reserved_4_39 : 36; +		u64 wait : 1; +		u64 reserved_0_2 : 3; +	} swork; +	struct { +		u64 mem_region : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 node : 4; +		u64 reserved_32_35 : 4; +		u64 indexed : 1; +		u64 grouped : 1; +		u64 rtngrp : 1; +		u64 reserved_16_28 : 13; +		u64 index : 12; +		u64 wait : 1; +		u64 reserved_0_2 : 3; +	} swork_78xx; +	/** +	 * Address for loads to get POW internal status +	 */ +	struct { +		u64 mem_region : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 reserved_10_39 : 30; +		u64 coreid : 4; +		u64 get_rev : 1; +		u64 get_cur : 1; +		u64 get_wqp : 1; +		u64 reserved_0_2 : 3; +	} sstatus; +	/** +	 * Address for loads to get 68XX SS0 internal status +	 */ +	struct { +		u64 mem_region : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 reserved_14_39 : 26; +		u64 coreid : 5; +		u64 reserved_6_8 : 3; +		u64 opcode : 3; +		u64 reserved_0_2 : 3; +	} sstatus_cn68xx; +	/** +	 * Address for memory loads to get POW internal state +	 */ +	struct { +		u64 mem_region : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 reserved_16_39 : 24; +		u64 index : 11; +		u64 get_des : 1; +		u64 get_wqp : 1; +		u64 reserved_0_2 : 3; +	} smemload; +	/** +	 * Address for memory loads to get SSO internal state +	 */ +	struct { +		u64 mem_region : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 reserved_20_39 : 20; +		u64 index : 11; +		u64 reserved_6_8 : 3; +		u64 opcode : 3; +		u64 reserved_0_2 : 3; +	} smemload_cn68xx; +	/** +	 * Address for index/pointer loads +	 */ +	struct { +		u64 mem_region : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 reserved_9_39 : 31; +		u64 qosgrp : 4; +		u64 get_des_get_tail : 1; +		u64 get_rmt : 1; +		u64 reserved_0_2 : 3; +	} sindexload; +	/** +	 * Address for a Index/Pointer loads to get SSO internal state +	 */ +	struct { +		u64 mem_region : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 reserved_15_39 : 25; +		u64 qos_grp : 6; +		u64 reserved_6_8 : 3; +		u64 opcode : 3; +		u64 reserved_0_2 : 3; +	} sindexload_cn68xx; +	/** +	 * Address for NULL_RD request (did<2:0> == 4) +	 * when this is read, HW attempts to change the state to NULL if it is NULL_NULL +	 * (the hardware cannot switch from NULL_NULL to NULL if a POW entry is not available - +	 * software may need to recover by finishing another piece of work before a POW +	 * entry can ever become available.) +	 */ +	struct { +		u64 mem_region : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 reserved_0_39 : 40; +	} snull_rd; +} cvmx_pow_load_addr_t; + +/** + * This structure defines the response to a load/SENDSINGLE to POW (except CSR reads) + */ +typedef union { +	u64 u64; +	/** +	 * Response to new work request loads +	 */ +	struct { +		u64 no_work : 1; +		u64 pend_switch : 1; +		u64 tt : 2; +		u64 reserved_58_59 : 2; +		u64 grp : 10; +		u64 reserved_42_47 : 6; +		u64 addr : 42; +	} s_work; + +	/** +	 * Result for a POW Status Load (when get_cur==0 and get_wqp==0) +	 */ +	struct { +		u64 reserved_62_63 : 2; +		u64 pend_switch : 1; +		u64 pend_switch_full : 1; +		u64 pend_switch_null : 1; +		u64 pend_desched : 1; +		u64 pend_desched_switch : 1; +		u64 pend_nosched : 1; +		u64 pend_new_work : 1; +		u64 pend_new_work_wait : 1; +		u64 pend_null_rd : 1; +		u64 pend_nosched_clr : 1; +		u64 reserved_51 : 1; +		u64 pend_index : 11; +		u64 pend_grp : 4; +		u64 reserved_34_35 : 2; +		u64 pend_type : 2; +		u64 pend_tag : 32; +	} s_sstatus0; +	/** +	 * Result for a SSO Status Load (when opcode is SL_PENDTAG) +	 */ +	struct { +		u64 pend_switch : 1; +		u64 pend_get_work : 1; +		u64 pend_get_work_wait : 1; +		u64 pend_nosched : 1; +		u64 pend_nosched_clr : 1; +		u64 pend_desched : 1; +		u64 pend_alloc_we : 1; +		u64 reserved_48_56 : 9; +		u64 pend_index : 11; +		u64 reserved_34_36 : 3; +		u64 pend_type : 2; +		u64 pend_tag : 32; +	} s_sstatus0_cn68xx; +	/** +	 * Result for a POW Status Load (when get_cur==0 and get_wqp==1) +	 */ +	struct { +		u64 reserved_62_63 : 2; +		u64 pend_switch : 1; +		u64 pend_switch_full : 1; +		u64 pend_switch_null : 1; +		u64 pend_desched : 1; +		u64 pend_desched_switch : 1; +		u64 pend_nosched : 1; +		u64 pend_new_work : 1; +		u64 pend_new_work_wait : 1; +		u64 pend_null_rd : 1; +		u64 pend_nosched_clr : 1; +		u64 reserved_51 : 1; +		u64 pend_index : 11; +		u64 pend_grp : 4; +		u64 pend_wqp : 36; +	} s_sstatus1; +	/** +	 * Result for a SSO Status Load (when opcode is SL_PENDWQP) +	 */ +	struct { +		u64 pend_switch : 1; +		u64 pend_get_work : 1; +		u64 pend_get_work_wait : 1; +		u64 pend_nosched : 1; +		u64 pend_nosched_clr : 1; +		u64 pend_desched : 1; +		u64 pend_alloc_we : 1; +		u64 reserved_51_56 : 6; +		u64 pend_index : 11; +		u64 reserved_38_39 : 2; +		u64 pend_wqp : 38; +	} s_sstatus1_cn68xx; + +	struct { +		u64 pend_switch : 1; +		u64 pend_get_work : 1; +		u64 pend_get_work_wait : 1; +		u64 pend_nosched : 1; +		u64 pend_nosched_clr : 1; +		u64 pend_desched : 1; +		u64 pend_alloc_we : 1; +		u64 reserved_56 : 1; +		u64 prep_index : 12; +		u64 reserved_42_43 : 2; +		u64 pend_tag : 42; +	} s_sso_ppx_pendwqp_cn78xx; +	/** +	 * Result for a POW Status Load (when get_cur==1, get_wqp==0, and get_rev==0) +	 */ +	struct { +		u64 reserved_62_63 : 2; +		u64 link_index : 11; +		u64 index : 11; +		u64 grp : 4; +		u64 head : 1; +		u64 tail : 1; +		u64 tag_type : 2; +		u64 tag : 32; +	} s_sstatus2; +	/** +	 * Result for a SSO Status Load (when opcode is SL_TAG) +	 */ +	struct { +		u64 reserved_57_63 : 7; +		u64 index : 11; +		u64 reserved_45 : 1; +		u64 grp : 6; +		u64 head : 1; +		u64 tail : 1; +		u64 reserved_34_36 : 3; +		u64 tag_type : 2; +		u64 tag : 32; +	} s_sstatus2_cn68xx; + +	struct { +		u64 tailc : 1; +		u64 reserved_60_62 : 3; +		u64 index : 12; +		u64 reserved_46_47 : 2; +		u64 grp : 10; +		u64 head : 1; +		u64 tail : 1; +		u64 tt : 2; +		u64 tag : 32; +	} s_sso_ppx_tag_cn78xx; +	/** +	 * Result for a POW Status Load (when get_cur==1, get_wqp==0, and get_rev==1) +	 */ +	struct { +		u64 reserved_62_63 : 2; +		u64 revlink_index : 11; +		u64 index : 11; +		u64 grp : 4; +		u64 head : 1; +		u64 tail : 1; +		u64 tag_type : 2; +		u64 tag : 32; +	} s_sstatus3; +	/** +	 * Result for a SSO Status Load (when opcode is SL_WQP) +	 */ +	struct { +		u64 reserved_58_63 : 6; +		u64 index : 11; +		u64 reserved_46 : 1; +		u64 grp : 6; +		u64 reserved_38_39 : 2; +		u64 wqp : 38; +	} s_sstatus3_cn68xx; + +	struct { +		u64 reserved_58_63 : 6; +		u64 grp : 10; +		u64 reserved_42_47 : 6; +		u64 tag : 42; +	} s_sso_ppx_wqp_cn78xx; +	/** +	 * Result for a POW Status Load (when get_cur==1, get_wqp==1, and get_rev==0) +	 */ +	struct { +		u64 reserved_62_63 : 2; +		u64 link_index : 11; +		u64 index : 11; +		u64 grp : 4; +		u64 wqp : 36; +	} s_sstatus4; +	/** +	 * Result for a SSO Status Load (when opcode is SL_LINKS) +	 */ +	struct { +		u64 reserved_46_63 : 18; +		u64 index : 11; +		u64 reserved_34 : 1; +		u64 grp : 6; +		u64 head : 1; +		u64 tail : 1; +		u64 reserved_24_25 : 2; +		u64 revlink_index : 11; +		u64 reserved_11_12 : 2; +		u64 link_index : 11; +	} s_sstatus4_cn68xx; + +	struct { +		u64 tailc : 1; +		u64 reserved_60_62 : 3; +		u64 index : 12; +		u64 reserved_38_47 : 10; +		u64 grp : 10; +		u64 head : 1; +		u64 tail : 1; +		u64 reserved_25 : 1; +		u64 revlink_index : 12; +		u64 link_index_vld : 1; +		u64 link_index : 12; +	} s_sso_ppx_links_cn78xx; +	/** +	 * Result for a POW Status Load (when get_cur==1, get_wqp==1, and get_rev==1) +	 */ +	struct { +		u64 reserved_62_63 : 2; +		u64 revlink_index : 11; +		u64 index : 11; +		u64 grp : 4; +		u64 wqp : 36; +	} s_sstatus5; +	/** +	 * Result For POW Memory Load (get_des == 0 and get_wqp == 0) +	 */ +	struct { +		u64 reserved_51_63 : 13; +		u64 next_index : 11; +		u64 grp : 4; +		u64 reserved_35 : 1; +		u64 tail : 1; +		u64 tag_type : 2; +		u64 tag : 32; +	} s_smemload0; +	/** +	 * Result For SSO Memory Load (opcode is ML_TAG) +	 */ +	struct { +		u64 reserved_38_63 : 26; +		u64 tail : 1; +		u64 reserved_34_36 : 3; +		u64 tag_type : 2; +		u64 tag : 32; +	} s_smemload0_cn68xx; + +	struct { +		u64 reserved_39_63 : 25; +		u64 tail : 1; +		u64 reserved_34_36 : 3; +		u64 tag_type : 2; +		u64 tag : 32; +	} s_sso_iaq_ppx_tag_cn78xx; +	/** +	 * Result For POW Memory Load (get_des == 0 and get_wqp == 1) +	 */ +	struct { +		u64 reserved_51_63 : 13; +		u64 next_index : 11; +		u64 grp : 4; +		u64 wqp : 36; +	} s_smemload1; +	/** +	 * Result For SSO Memory Load (opcode is ML_WQPGRP) +	 */ +	struct { +		u64 reserved_48_63 : 16; +		u64 nosched : 1; +		u64 reserved_46 : 1; +		u64 grp : 6; +		u64 reserved_38_39 : 2; +		u64 wqp : 38; +	} s_smemload1_cn68xx; + +	/** +	 * Entry structures for the CN7XXX chips. +	 */ +	struct { +		u64 reserved_39_63 : 25; +		u64 tailc : 1; +		u64 tail : 1; +		u64 reserved_34_36 : 3; +		u64 tt : 2; +		u64 tag : 32; +	} s_sso_ientx_tag_cn78xx; + +	struct { +		u64 reserved_62_63 : 2; +		u64 head : 1; +		u64 nosched : 1; +		u64 reserved_56_59 : 4; +		u64 grp : 8; +		u64 reserved_42_47 : 6; +		u64 wqp : 42; +	} s_sso_ientx_wqpgrp_cn73xx; + +	struct { +		u64 reserved_62_63 : 2; +		u64 head : 1; +		u64 nosched : 1; +		u64 reserved_58_59 : 2; +		u64 grp : 10; +		u64 reserved_42_47 : 6; +		u64 wqp : 42; +	} s_sso_ientx_wqpgrp_cn78xx; + +	struct { +		u64 reserved_38_63 : 26; +		u64 pend_switch : 1; +		u64 reserved_34_36 : 3; +		u64 pend_tt : 2; +		u64 pend_tag : 32; +	} s_sso_ientx_pendtag_cn78xx; + +	struct { +		u64 reserved_26_63 : 38; +		u64 prev_index : 10; +		u64 reserved_11_15 : 5; +		u64 next_index_vld : 1; +		u64 next_index : 10; +	} s_sso_ientx_links_cn73xx; + +	struct { +		u64 reserved_28_63 : 36; +		u64 prev_index : 12; +		u64 reserved_13_15 : 3; +		u64 next_index_vld : 1; +		u64 next_index : 12; +	} s_sso_ientx_links_cn78xx; + +	/** +	 * Result For POW Memory Load (get_des == 1) +	 */ +	struct { +		u64 reserved_51_63 : 13; +		u64 fwd_index : 11; +		u64 grp : 4; +		u64 nosched : 1; +		u64 pend_switch : 1; +		u64 pend_type : 2; +		u64 pend_tag : 32; +	} s_smemload2; +	/** +	 * Result For SSO Memory Load (opcode is ML_PENTAG) +	 */ +	struct { +		u64 reserved_38_63 : 26; +		u64 pend_switch : 1; +		u64 reserved_34_36 : 3; +		u64 pend_type : 2; +		u64 pend_tag : 32; +	} s_smemload2_cn68xx; + +	struct { +		u64 pend_switch : 1; +		u64 pend_get_work : 1; +		u64 pend_get_work_wait : 1; +		u64 pend_nosched : 1; +		u64 pend_nosched_clr : 1; +		u64 pend_desched : 1; +		u64 pend_alloc_we : 1; +		u64 reserved_34_56 : 23; +		u64 pend_tt : 2; +		u64 pend_tag : 32; +	} s_sso_ppx_pendtag_cn78xx; +	/** +	 * Result For SSO Memory Load (opcode is ML_LINKS) +	 */ +	struct { +		u64 reserved_24_63 : 40; +		u64 fwd_index : 11; +		u64 reserved_11_12 : 2; +		u64 next_index : 11; +	} s_smemload3_cn68xx; + +	/** +	 * Result For POW Index/Pointer Load (get_rmt == 0/get_des_get_tail == 0) +	 */ +	struct { +		u64 reserved_52_63 : 12; +		u64 free_val : 1; +		u64 free_one : 1; +		u64 reserved_49 : 1; +		u64 free_head : 11; +		u64 reserved_37 : 1; +		u64 free_tail : 11; +		u64 loc_val : 1; +		u64 loc_one : 1; +		u64 reserved_23 : 1; +		u64 loc_head : 11; +		u64 reserved_11 : 1; +		u64 loc_tail : 11; +	} sindexload0; +	/** +	 * Result for SSO Index/Pointer Load(opcode == +	 * IPL_IQ/IPL_DESCHED/IPL_NOSCHED) +	 */ +	struct { +		u64 reserved_28_63 : 36; +		u64 queue_val : 1; +		u64 queue_one : 1; +		u64 reserved_24_25 : 2; +		u64 queue_head : 11; +		u64 reserved_11_12 : 2; +		u64 queue_tail : 11; +	} sindexload0_cn68xx; +	/** +	 * Result For POW Index/Pointer Load (get_rmt == 0/get_des_get_tail == 1) +	 */ +	struct { +		u64 reserved_52_63 : 12; +		u64 nosched_val : 1; +		u64 nosched_one : 1; +		u64 reserved_49 : 1; +		u64 nosched_head : 11; +		u64 reserved_37 : 1; +		u64 nosched_tail : 11; +		u64 des_val : 1; +		u64 des_one : 1; +		u64 reserved_23 : 1; +		u64 des_head : 11; +		u64 reserved_11 : 1; +		u64 des_tail : 11; +	} sindexload1; +	/** +	 * Result for SSO Index/Pointer Load(opcode == IPL_FREE0/IPL_FREE1/IPL_FREE2) +	 */ +	struct { +		u64 reserved_60_63 : 4; +		u64 qnum_head : 2; +		u64 qnum_tail : 2; +		u64 reserved_28_55 : 28; +		u64 queue_val : 1; +		u64 queue_one : 1; +		u64 reserved_24_25 : 2; +		u64 queue_head : 11; +		u64 reserved_11_12 : 2; +		u64 queue_tail : 11; +	} sindexload1_cn68xx; +	/** +	 * Result For POW Index/Pointer Load (get_rmt == 1/get_des_get_tail == 0) +	 */ +	struct { +		u64 reserved_39_63 : 25; +		u64 rmt_is_head : 1; +		u64 rmt_val : 1; +		u64 rmt_one : 1; +		u64 rmt_head : 36; +	} sindexload2; +	/** +	 * Result For POW Index/Pointer Load (get_rmt == 1/get_des_get_tail == 1) +	 */ +	struct { +		u64 reserved_39_63 : 25; +		u64 rmt_is_head : 1; +		u64 rmt_val : 1; +		u64 rmt_one : 1; +		u64 rmt_tail : 36; +	} sindexload3; +	/** +	 * Response to NULL_RD request loads +	 */ +	struct { +		u64 unused : 62; +		u64 state : 2; +	} s_null_rd; + +} cvmx_pow_tag_load_resp_t; + +typedef union { +	u64 u64; +	struct { +		u64 reserved_57_63 : 7; +		u64 index : 11; +		u64 reserved_45 : 1; +		u64 grp : 6; +		u64 head : 1; +		u64 tail : 1; +		u64 reserved_34_36 : 3; +		u64 tag_type : 2; +		u64 tag : 32; +	} s; +} cvmx_pow_sl_tag_resp_t; + +/** + * This structure describes the address used for stores to the POW. + *  The store address is meaningful on stores to the POW.  The hardware assumes that an aligned + *  64-bit store was used for all these stores. + *  Note the assumption that the work queue entry is aligned on an 8-byte + *  boundary (since the low-order 3 address bits must be zero). + *  Note that not all fields are used by all operations. + * + *  NOTE: The following is the behavior of the pending switch bit at the PP + *       for POW stores (i.e. when did<7:3> == 0xc) + *     - did<2:0> == 0      => pending switch bit is set + *     - did<2:0> == 1      => no affect on the pending switch bit + *     - did<2:0> == 3      => pending switch bit is cleared + *     - did<2:0> == 7      => no affect on the pending switch bit + *     - did<2:0> == others => must not be used + *     - No other loads/stores have an affect on the pending switch bit + *     - The switch bus from POW can clear the pending switch bit + * + *  NOTE: did<2:0> == 2 is used by the HW for a special single-cycle ADDWQ command + *  that only contains the pointer). SW must never use did<2:0> == 2. + */ +typedef union { +	u64 u64; +	struct { +		u64 mem_reg : 2; +		u64 reserved_49_61 : 13; +		u64 is_io : 1; +		u64 did : 8; +		u64 addr : 40; +	} stag; +} cvmx_pow_tag_store_addr_t; /* FIXME- this type is unused */ + +/** + * Decode of the store data when an IOBDMA SENDSINGLE is sent to POW + */ +typedef union { +	u64 u64; +	struct { +		u64 scraddr : 8; +		u64 len : 8; +		u64 did : 8; +		u64 unused : 36; +		u64 wait : 1; +		u64 unused2 : 3; +	} s; +	struct { +		u64 scraddr : 8; +		u64 len : 8; +		u64 did : 8; +		u64 node : 4; +		u64 unused1 : 4; +		u64 indexed : 1; +		u64 grouped : 1; +		u64 rtngrp : 1; +		u64 unused2 : 13; +		u64 index_grp_mask : 12; +		u64 wait : 1; +		u64 unused3 : 3; +	} s_cn78xx; +} cvmx_pow_iobdma_store_t; + +/* CSR typedefs have been moved to cvmx-pow-defs.h */ + +/*enum for group priority parameters which needs modification*/ +enum cvmx_sso_group_modify_mask { +	CVMX_SSO_MODIFY_GROUP_PRIORITY = 0x01, +	CVMX_SSO_MODIFY_GROUP_WEIGHT = 0x02, +	CVMX_SSO_MODIFY_GROUP_AFFINITY = 0x04 +}; + +/** + * @INTERNAL + * Return the number of SSO groups for a given SoC model + */ +static inline unsigned int cvmx_sso_num_xgrp(void) +{ +	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) +		return 256; +	if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) +		return 64; +	if (OCTEON_IS_MODEL(OCTEON_CN73XX)) +		return 64; +	printf("ERROR: %s: Unknown model\n", __func__); +	return 0; +} + +/** + * @INTERNAL + * Return the number of POW groups on current model. + * In case of CN78XX/CN73XX this is the number of equivalent + * "legacy groups" on the chip when it is used in backward + * compatible mode. + */ +static inline unsigned int cvmx_pow_num_groups(void) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		return cvmx_sso_num_xgrp() >> 3; +	else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) +		return 64; +	else +		return 16; +} + +/** + * @INTERNAL + * Return the number of mask-set registers. + */ +static inline unsigned int cvmx_sso_num_maskset(void) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		return 2; +	else +		return 1; +} + +/** + * Get the POW tag for this core. This returns the current + * tag type, tag, group, and POW entry index associated with + * this core. Index is only valid if the tag type isn't NULL_NULL. + * If a tag switch is pending this routine returns the tag before + * the tag switch, not after. + * + * @return Current tag + */ +static inline cvmx_pow_tag_info_t cvmx_pow_get_current_tag(void) +{ +	cvmx_pow_load_addr_t load_addr; +	cvmx_pow_tag_info_t result; + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_sso_sl_ppx_tag_t sl_ppx_tag; +		cvmx_xgrp_t xgrp; +		int node, core; + +		CVMX_SYNCS; +		node = cvmx_get_node_num(); +		core = cvmx_get_local_core_num(); +		sl_ppx_tag.u64 = csr_rd_node(node, CVMX_SSO_SL_PPX_TAG(core)); +		result.index = sl_ppx_tag.s.index; +		result.tag_type = sl_ppx_tag.s.tt; +		result.tag = sl_ppx_tag.s.tag; + +		/* Get native XGRP value */ +		xgrp.xgrp = sl_ppx_tag.s.grp; + +		/* Return legacy style group 0..15 */ +		result.grp = xgrp.group; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		cvmx_pow_sl_tag_resp_t load_resp; + +		load_addr.u64 = 0; +		load_addr.sstatus_cn68xx.mem_region = CVMX_IO_SEG; +		load_addr.sstatus_cn68xx.is_io = 1; +		load_addr.sstatus_cn68xx.did = CVMX_OCT_DID_TAG_TAG5; +		load_addr.sstatus_cn68xx.coreid = cvmx_get_core_num(); +		load_addr.sstatus_cn68xx.opcode = 3; +		load_resp.u64 = csr_rd(load_addr.u64); +		result.grp = load_resp.s.grp; +		result.index = load_resp.s.index; +		result.tag_type = load_resp.s.tag_type; +		result.tag = load_resp.s.tag; +	} else { +		cvmx_pow_tag_load_resp_t load_resp; + +		load_addr.u64 = 0; +		load_addr.sstatus.mem_region = CVMX_IO_SEG; +		load_addr.sstatus.is_io = 1; +		load_addr.sstatus.did = CVMX_OCT_DID_TAG_TAG1; +		load_addr.sstatus.coreid = cvmx_get_core_num(); +		load_addr.sstatus.get_cur = 1; +		load_resp.u64 = csr_rd(load_addr.u64); +		result.grp = load_resp.s_sstatus2.grp; +		result.index = load_resp.s_sstatus2.index; +		result.tag_type = load_resp.s_sstatus2.tag_type; +		result.tag = load_resp.s_sstatus2.tag; +	} +	return result; +} + +/** + * Get the POW WQE for this core. This returns the work queue + * entry currently associated with this core. + * + * @return WQE pointer + */ +static inline cvmx_wqe_t *cvmx_pow_get_current_wqp(void) +{ +	cvmx_pow_load_addr_t load_addr; +	cvmx_pow_tag_load_resp_t load_resp; + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_sso_sl_ppx_wqp_t sso_wqp; +		int node = cvmx_get_node_num(); +		int core = cvmx_get_local_core_num(); + +		sso_wqp.u64 = csr_rd_node(node, CVMX_SSO_SL_PPX_WQP(core)); +		if (sso_wqp.s.wqp) +			return (cvmx_wqe_t *)cvmx_phys_to_ptr(sso_wqp.s.wqp); +		return (cvmx_wqe_t *)0; +	} +	if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		load_addr.u64 = 0; +		load_addr.sstatus_cn68xx.mem_region = CVMX_IO_SEG; +		load_addr.sstatus_cn68xx.is_io = 1; +		load_addr.sstatus_cn68xx.did = CVMX_OCT_DID_TAG_TAG5; +		load_addr.sstatus_cn68xx.coreid = cvmx_get_core_num(); +		load_addr.sstatus_cn68xx.opcode = 4; +		load_resp.u64 = csr_rd(load_addr.u64); +		if (load_resp.s_sstatus3_cn68xx.wqp) +			return (cvmx_wqe_t *)cvmx_phys_to_ptr(load_resp.s_sstatus3_cn68xx.wqp); +		else +			return (cvmx_wqe_t *)0; +	} else { +		load_addr.u64 = 0; +		load_addr.sstatus.mem_region = CVMX_IO_SEG; +		load_addr.sstatus.is_io = 1; +		load_addr.sstatus.did = CVMX_OCT_DID_TAG_TAG1; +		load_addr.sstatus.coreid = cvmx_get_core_num(); +		load_addr.sstatus.get_cur = 1; +		load_addr.sstatus.get_wqp = 1; +		load_resp.u64 = csr_rd(load_addr.u64); +		return (cvmx_wqe_t *)cvmx_phys_to_ptr(load_resp.s_sstatus4.wqp); +	} +} + +/** + * @INTERNAL + * Print a warning if a tag switch is pending for this core + * + * @param function Function name checking for a pending tag switch + */ +static inline void __cvmx_pow_warn_if_pending_switch(const char *function) +{ +	u64 switch_complete; + +	CVMX_MF_CHORD(switch_complete); +	cvmx_warn_if(!switch_complete, "%s called with tag switch in progress\n", function); +} + +/** + * Waits for a tag switch to complete by polling the completion bit. + * Note that switches to NULL complete immediately and do not need + * to be waited for. + */ +static inline void cvmx_pow_tag_sw_wait(void) +{ +	const u64 TIMEOUT_MS = 10; /* 10ms timeout */ +	u64 switch_complete; +	u64 start_cycle; + +	if (CVMX_ENABLE_POW_CHECKS) +		start_cycle = get_timer(0); + +	while (1) { +		CVMX_MF_CHORD(switch_complete); +		if (cvmx_likely(switch_complete)) +			break; + +		if (CVMX_ENABLE_POW_CHECKS) { +			if (cvmx_unlikely(get_timer(start_cycle) > TIMEOUT_MS)) { +				debug("WARNING: %s: Tag switch is taking a long time, possible deadlock\n", +				      __func__); +			} +		} +	} +} + +/** + * Synchronous work request.  Requests work from the POW. + * This function does NOT wait for previous tag switches to complete, + * so the caller must ensure that there is not a pending tag switch. + * + * @param wait   When set, call stalls until work becomes available, or + *               times out. If not set, returns immediately. + * + * @return Returns the WQE pointer from POW. Returns NULL if no work was + * available. + */ +static inline cvmx_wqe_t *cvmx_pow_work_request_sync_nocheck(cvmx_pow_wait_t wait) +{ +	cvmx_pow_load_addr_t ptr; +	cvmx_pow_tag_load_resp_t result; + +	if (CVMX_ENABLE_POW_CHECKS) +		__cvmx_pow_warn_if_pending_switch(__func__); + +	ptr.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		ptr.swork_78xx.node = cvmx_get_node_num(); +		ptr.swork_78xx.mem_region = CVMX_IO_SEG; +		ptr.swork_78xx.is_io = 1; +		ptr.swork_78xx.did = CVMX_OCT_DID_TAG_SWTAG; +		ptr.swork_78xx.wait = wait; +	} else { +		ptr.swork.mem_region = CVMX_IO_SEG; +		ptr.swork.is_io = 1; +		ptr.swork.did = CVMX_OCT_DID_TAG_SWTAG; +		ptr.swork.wait = wait; +	} + +	result.u64 = csr_rd(ptr.u64); +	if (result.s_work.no_work) +		return NULL; +	else +		return (cvmx_wqe_t *)cvmx_phys_to_ptr(result.s_work.addr); +} + +/** + * Synchronous work request.  Requests work from the POW. + * This function waits for any previous tag switch to complete before + * requesting the new work. + * + * @param wait   When set, call stalls until work becomes available, or + *               times out. If not set, returns immediately. + * + * @return Returns the WQE pointer from POW. Returns NULL if no work was + * available. + */ +static inline cvmx_wqe_t *cvmx_pow_work_request_sync(cvmx_pow_wait_t wait) +{ +	/* Must not have a switch pending when requesting work */ +	cvmx_pow_tag_sw_wait(); +	return (cvmx_pow_work_request_sync_nocheck(wait)); +} + +/** + * Synchronous null_rd request.  Requests a switch out of NULL_NULL POW state. + * This function waits for any previous tag switch to complete before + * requesting the null_rd. + * + * @return Returns the POW state of type cvmx_pow_tag_type_t. + */ +static inline cvmx_pow_tag_type_t cvmx_pow_work_request_null_rd(void) +{ +	cvmx_pow_load_addr_t ptr; +	cvmx_pow_tag_load_resp_t result; + +	/* Must not have a switch pending when requesting work */ +	cvmx_pow_tag_sw_wait(); + +	ptr.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		ptr.swork_78xx.mem_region = CVMX_IO_SEG; +		ptr.swork_78xx.is_io = 1; +		ptr.swork_78xx.did = CVMX_OCT_DID_TAG_NULL_RD; +		ptr.swork_78xx.node = cvmx_get_node_num(); +	} else { +		ptr.snull_rd.mem_region = CVMX_IO_SEG; +		ptr.snull_rd.is_io = 1; +		ptr.snull_rd.did = CVMX_OCT_DID_TAG_NULL_RD; +	} +	result.u64 = csr_rd(ptr.u64); +	return (cvmx_pow_tag_type_t)result.s_null_rd.state; +} + +/** + * Asynchronous work request. + * Work is requested from the POW unit, and should later be checked with + * function cvmx_pow_work_response_async. + * This function does NOT wait for previous tag switches to complete, + * so the caller must ensure that there is not a pending tag switch. + * + * @param scr_addr Scratch memory address that response will be returned to, + *     which is either a valid WQE, or a response with the invalid bit set. + *     Byte address, must be 8 byte aligned. + * @param wait 1 to cause response to wait for work to become available + *               (or timeout) + *             0 to cause response to return immediately + */ +static inline void cvmx_pow_work_request_async_nocheck(int scr_addr, cvmx_pow_wait_t wait) +{ +	cvmx_pow_iobdma_store_t data; + +	if (CVMX_ENABLE_POW_CHECKS) +		__cvmx_pow_warn_if_pending_switch(__func__); + +	/* scr_addr must be 8 byte aligned */ +	data.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		data.s_cn78xx.node = cvmx_get_node_num(); +		data.s_cn78xx.scraddr = scr_addr >> 3; +		data.s_cn78xx.len = 1; +		data.s_cn78xx.did = CVMX_OCT_DID_TAG_SWTAG; +		data.s_cn78xx.wait = wait; +	} else { +		data.s.scraddr = scr_addr >> 3; +		data.s.len = 1; +		data.s.did = CVMX_OCT_DID_TAG_SWTAG; +		data.s.wait = wait; +	} +	cvmx_send_single(data.u64); +} + +/** + * Asynchronous work request. + * Work is requested from the POW unit, and should later be checked with + * function cvmx_pow_work_response_async. + * This function waits for any previous tag switch to complete before + * requesting the new work. + * + * @param scr_addr Scratch memory address that response will be returned to, + *     which is either a valid WQE, or a response with the invalid bit set. + *     Byte address, must be 8 byte aligned. + * @param wait 1 to cause response to wait for work to become available + *               (or timeout) + *             0 to cause response to return immediately + */ +static inline void cvmx_pow_work_request_async(int scr_addr, cvmx_pow_wait_t wait) +{ +	/* Must not have a switch pending when requesting work */ +	cvmx_pow_tag_sw_wait(); +	cvmx_pow_work_request_async_nocheck(scr_addr, wait); +} + +/** + * Gets result of asynchronous work request.  Performs a IOBDMA sync + * to wait for the response. + * + * @param scr_addr Scratch memory address to get result from + *                  Byte address, must be 8 byte aligned. + * @return Returns the WQE from the scratch register, or NULL if no work was + *         available. + */ +static inline cvmx_wqe_t *cvmx_pow_work_response_async(int scr_addr) +{ +	cvmx_pow_tag_load_resp_t result; + +	CVMX_SYNCIOBDMA; +	result.u64 = cvmx_scratch_read64(scr_addr); +	if (result.s_work.no_work) +		return NULL; +	else +		return (cvmx_wqe_t *)cvmx_phys_to_ptr(result.s_work.addr); +} + +/** + * Checks if a work queue entry pointer returned by a work + * request is valid.  It may be invalid due to no work + * being available or due to a timeout. + * + * @param wqe_ptr pointer to a work queue entry returned by the POW + * + * @return 0 if pointer is valid + *         1 if invalid (no work was returned) + */ +static inline u64 cvmx_pow_work_invalid(cvmx_wqe_t *wqe_ptr) +{ +	return (!wqe_ptr); /* FIXME: improve */ +} + +/** + * Starts a tag switch to the provided tag value and tag type.  Completion for + * the tag switch must be checked for separately. + * This function does NOT update the + * work queue entry in dram to match tag value and type, so the application must + * keep track of these if they are important to the application. + * This tag switch command must not be used for switches to NULL, as the tag + * switch pending bit will be set by the switch request, but never cleared by + * the hardware. + * + * NOTE: This should not be used when switching from a NULL tag.  Use + * cvmx_pow_tag_sw_full() instead. + * + * This function does no checks, so the caller must ensure that any previous tag + * switch has completed. + * + * @param tag      new tag value + * @param tag_type new tag type (ordered or atomic) + */ +static inline void cvmx_pow_tag_sw_nocheck(u32 tag, cvmx_pow_tag_type_t tag_type) +{ +	union cvmx_pow_tag_req_addr ptr; +	cvmx_pow_tag_req_t tag_req; + +	if (CVMX_ENABLE_POW_CHECKS) { +		cvmx_pow_tag_info_t current_tag; + +		__cvmx_pow_warn_if_pending_switch(__func__); +		current_tag = cvmx_pow_get_current_tag(); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL_NULL, +			     "%s called with NULL_NULL tag\n", __func__); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL, +			     "%s called with NULL tag\n", __func__); +		cvmx_warn_if((current_tag.tag_type == tag_type) && (current_tag.tag == tag), +			     "%s called to perform a tag switch to the same tag\n", __func__); +		cvmx_warn_if( +			tag_type == CVMX_POW_TAG_TYPE_NULL, +			"%s called to perform a tag switch to NULL. Use cvmx_pow_tag_sw_null() instead\n", +			__func__); +	} + +	/* +	 * Note that WQE in DRAM is not updated here, as the POW does not read +	 * from DRAM once the WQE is in flight.  See hardware manual for +	 * complete details. +	 * It is the application's responsibility to keep track of the +	 * current tag value if that is important. +	 */ +	tag_req.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		tag_req.s_cn78xx_other.op = CVMX_POW_TAG_OP_SWTAG; +		tag_req.s_cn78xx_other.type = tag_type; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		tag_req.s_cn68xx_other.op = CVMX_POW_TAG_OP_SWTAG; +		tag_req.s_cn68xx_other.tag = tag; +		tag_req.s_cn68xx_other.type = tag_type; +	} else { +		tag_req.s_cn38xx.op = CVMX_POW_TAG_OP_SWTAG; +		tag_req.s_cn38xx.tag = tag; +		tag_req.s_cn38xx.type = tag_type; +	} +	ptr.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		ptr.s_cn78xx.mem_region = CVMX_IO_SEG; +		ptr.s_cn78xx.is_io = 1; +		ptr.s_cn78xx.did = CVMX_OCT_DID_TAG_SWTAG; +		ptr.s_cn78xx.node = cvmx_get_node_num(); +		ptr.s_cn78xx.tag = tag; +	} else { +		ptr.s.mem_region = CVMX_IO_SEG; +		ptr.s.is_io = 1; +		ptr.s.did = CVMX_OCT_DID_TAG_SWTAG; +	} +	/* Once this store arrives at POW, it will attempt the switch +	   software must wait for the switch to complete separately */ +	cvmx_write_io(ptr.u64, tag_req.u64); +} + +/** + * Starts a tag switch to the provided tag value and tag type.  Completion for + * the tag switch must be checked for separately. + * This function does NOT update the + * work queue entry in dram to match tag value and type, so the application must + * keep track of these if they are important to the application. + * This tag switch command must not be used for switches to NULL, as the tag + * switch pending bit will be set by the switch request, but never cleared by + * the hardware. + * + * NOTE: This should not be used when switching from a NULL tag.  Use + * cvmx_pow_tag_sw_full() instead. + * + * This function waits for any previous tag switch to complete, and also + * displays an error on tag switches to NULL. + * + * @param tag      new tag value + * @param tag_type new tag type (ordered or atomic) + */ +static inline void cvmx_pow_tag_sw(u32 tag, cvmx_pow_tag_type_t tag_type) +{ +	/* +	 * Note that WQE in DRAM is not updated here, as the POW does not read +	 * from DRAM once the WQE is in flight.  See hardware manual for +	 * complete details. It is the application's responsibility to keep +	 * track of the current tag value if that is important. +	 */ + +	/* +	 * Ensure that there is not a pending tag switch, as a tag switch +	 * cannot be started if a previous switch is still pending. +	 */ +	cvmx_pow_tag_sw_wait(); +	cvmx_pow_tag_sw_nocheck(tag, tag_type); +} + +/** + * Starts a tag switch to the provided tag value and tag type.  Completion for + * the tag switch must be checked for separately. + * This function does NOT update the + * work queue entry in dram to match tag value and type, so the application must + * keep track of these if they are important to the application. + * This tag switch command must not be used for switches to NULL, as the tag + * switch pending bit will be set by the switch request, but never cleared by + * the hardware. + * + * This function must be used for tag switches from NULL. + * + * This function does no checks, so the caller must ensure that any previous tag + * switch has completed. + * + * @param wqp      pointer to work queue entry to submit.  This entry is + *                 updated to match the other parameters + * @param tag      tag value to be assigned to work queue entry + * @param tag_type type of tag + * @param group    group value for the work queue entry. + */ +static inline void cvmx_pow_tag_sw_full_nocheck(cvmx_wqe_t *wqp, u32 tag, +						cvmx_pow_tag_type_t tag_type, u64 group) +{ +	union cvmx_pow_tag_req_addr ptr; +	cvmx_pow_tag_req_t tag_req; +	unsigned int node = cvmx_get_node_num(); +	u64 wqp_phys = cvmx_ptr_to_phys(wqp); + +	if (CVMX_ENABLE_POW_CHECKS) { +		cvmx_pow_tag_info_t current_tag; + +		__cvmx_pow_warn_if_pending_switch(__func__); +		current_tag = cvmx_pow_get_current_tag(); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL_NULL, +			     "%s called with NULL_NULL tag\n", __func__); +		cvmx_warn_if((current_tag.tag_type == tag_type) && (current_tag.tag == tag), +			     "%s called to perform a tag switch to the same tag\n", __func__); +		cvmx_warn_if( +			tag_type == CVMX_POW_TAG_TYPE_NULL, +			"%s called to perform a tag switch to NULL. Use cvmx_pow_tag_sw_null() instead\n", +			__func__); +		if ((wqp != cvmx_phys_to_ptr(0x80)) && cvmx_pow_get_current_wqp()) +			cvmx_warn_if(wqp != cvmx_pow_get_current_wqp(), +				     "%s passed WQE(%p) doesn't match the address in the POW(%p)\n", +				     __func__, wqp, cvmx_pow_get_current_wqp()); +	} + +	/* +	 * Note that WQE in DRAM is not updated here, as the POW does not +	 * read from DRAM once the WQE is in flight.  See hardware manual +	 * for complete details. It is the application's responsibility to +	 * keep track of the current tag value if that is important. +	 */ +	tag_req.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		unsigned int xgrp; + +		if (wqp_phys != 0x80) { +			/* If WQE is valid, use its XGRP: +			 * WQE GRP is 10 bits, and is mapped +			 * to legacy GRP + QoS, includes node number. +			 */ +			xgrp = wqp->word1.cn78xx.grp; +			/* Use XGRP[node] too */ +			node = xgrp >> 8; +			/* Modify XGRP with legacy group # from arg */ +			xgrp &= ~0xf8; +			xgrp |= 0xf8 & (group << 3); + +		} else { +			/* If no WQE, build XGRP with QoS=0 and current node */ +			xgrp = group << 3; +			xgrp |= node << 8; +		} +		tag_req.s_cn78xx_other.op = CVMX_POW_TAG_OP_SWTAG_FULL; +		tag_req.s_cn78xx_other.type = tag_type; +		tag_req.s_cn78xx_other.grp = xgrp; +		tag_req.s_cn78xx_other.wqp = wqp_phys; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		tag_req.s_cn68xx_other.op = CVMX_POW_TAG_OP_SWTAG_FULL; +		tag_req.s_cn68xx_other.tag = tag; +		tag_req.s_cn68xx_other.type = tag_type; +		tag_req.s_cn68xx_other.grp = group; +	} else { +		tag_req.s_cn38xx.op = CVMX_POW_TAG_OP_SWTAG_FULL; +		tag_req.s_cn38xx.tag = tag; +		tag_req.s_cn38xx.type = tag_type; +		tag_req.s_cn38xx.grp = group; +	} +	ptr.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		ptr.s_cn78xx.mem_region = CVMX_IO_SEG; +		ptr.s_cn78xx.is_io = 1; +		ptr.s_cn78xx.did = CVMX_OCT_DID_TAG_SWTAG; +		ptr.s_cn78xx.node = node; +		ptr.s_cn78xx.tag = tag; +	} else { +		ptr.s.mem_region = CVMX_IO_SEG; +		ptr.s.is_io = 1; +		ptr.s.did = CVMX_OCT_DID_TAG_SWTAG; +		ptr.s.addr = wqp_phys; +	} +	/* Once this store arrives at POW, it will attempt the switch +	   software must wait for the switch to complete separately */ +	cvmx_write_io(ptr.u64, tag_req.u64); +} + +/** + * Starts a tag switch to the provided tag value and tag type. + * Completion for the tag switch must be checked for separately. + * This function does NOT update the work queue entry in dram to match tag value + * and type, so the application must keep track of these if they are important + * to the application. This tag switch command must not be used for switches + * to NULL, as the tag switch pending bit will be set by the switch request, + * but never cleared by the hardware. + * + * This function must be used for tag switches from NULL. + * + * This function waits for any pending tag switches to complete + * before requesting the tag switch. + * + * @param wqp      Pointer to work queue entry to submit. + *     This entry is updated to match the other parameters + * @param tag      Tag value to be assigned to work queue entry + * @param tag_type Type of tag + * @param group    Group value for the work queue entry. + */ +static inline void cvmx_pow_tag_sw_full(cvmx_wqe_t *wqp, u32 tag, cvmx_pow_tag_type_t tag_type, +					u64 group) +{ +	/* +	 * Ensure that there is not a pending tag switch, as a tag switch cannot +	 * be started if a previous switch is still pending. +	 */ +	cvmx_pow_tag_sw_wait(); +	cvmx_pow_tag_sw_full_nocheck(wqp, tag, tag_type, group); +} + +/** + * Switch to a NULL tag, which ends any ordering or + * synchronization provided by the POW for the current + * work queue entry.  This operation completes immediately, + * so completion should not be waited for. + * This function does NOT wait for previous tag switches to complete, + * so the caller must ensure that any previous tag switches have completed. + */ +static inline void cvmx_pow_tag_sw_null_nocheck(void) +{ +	union cvmx_pow_tag_req_addr ptr; +	cvmx_pow_tag_req_t tag_req; + +	if (CVMX_ENABLE_POW_CHECKS) { +		cvmx_pow_tag_info_t current_tag; + +		__cvmx_pow_warn_if_pending_switch(__func__); +		current_tag = cvmx_pow_get_current_tag(); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL_NULL, +			     "%s called with NULL_NULL tag\n", __func__); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL, +			     "%s called when we already have a NULL tag\n", __func__); +	} +	tag_req.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		tag_req.s_cn78xx_other.op = CVMX_POW_TAG_OP_SWTAG; +		tag_req.s_cn78xx_other.type = CVMX_POW_TAG_TYPE_NULL; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		tag_req.s_cn68xx_other.op = CVMX_POW_TAG_OP_SWTAG; +		tag_req.s_cn68xx_other.type = CVMX_POW_TAG_TYPE_NULL; +	} else { +		tag_req.s_cn38xx.op = CVMX_POW_TAG_OP_SWTAG; +		tag_req.s_cn38xx.type = CVMX_POW_TAG_TYPE_NULL; +	} +	ptr.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		ptr.s_cn78xx.mem_region = CVMX_IO_SEG; +		ptr.s_cn78xx.is_io = 1; +		ptr.s_cn78xx.did = CVMX_OCT_DID_TAG_TAG1; +		ptr.s_cn78xx.node = cvmx_get_node_num(); +	} else { +		ptr.s.mem_region = CVMX_IO_SEG; +		ptr.s.is_io = 1; +		ptr.s.did = CVMX_OCT_DID_TAG_TAG1; +	} +	cvmx_write_io(ptr.u64, tag_req.u64); +} + +/** + * Switch to a NULL tag, which ends any ordering or + * synchronization provided by the POW for the current + * work queue entry.  This operation completes immediately, + * so completion should not be waited for. + * This function waits for any pending tag switches to complete + * before requesting the switch to NULL. + */ +static inline void cvmx_pow_tag_sw_null(void) +{ +	/* +	 * Ensure that there is not a pending tag switch, as a tag switch cannot +	 * be started if a previous switch is still pending. +	 */ +	cvmx_pow_tag_sw_wait(); +	cvmx_pow_tag_sw_null_nocheck(); +} + +/** + * Submits work to an input queue. + * This function updates the work queue entry in DRAM to match the arguments given. + * Note that the tag provided is for the work queue entry submitted, and + * is unrelated to the tag that the core currently holds. + * + * @param wqp      pointer to work queue entry to submit. + *                 This entry is updated to match the other parameters + * @param tag      tag value to be assigned to work queue entry + * @param tag_type type of tag + * @param qos      Input queue to add to. + * @param grp      group value for the work queue entry. + */ +static inline void cvmx_pow_work_submit(cvmx_wqe_t *wqp, u32 tag, cvmx_pow_tag_type_t tag_type, +					u64 qos, u64 grp) +{ +	union cvmx_pow_tag_req_addr ptr; +	cvmx_pow_tag_req_t tag_req; + +	tag_req.u64 = 0; +	ptr.u64 = 0; + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		unsigned int node = cvmx_get_node_num(); +		unsigned int xgrp; + +		xgrp = (grp & 0x1f) << 3; +		xgrp |= (qos & 7); +		xgrp |= 0x300 & (node << 8); + +		wqp->word1.cn78xx.rsvd_0 = 0; +		wqp->word1.cn78xx.rsvd_1 = 0; +		wqp->word1.cn78xx.tag = tag; +		wqp->word1.cn78xx.tag_type = tag_type; +		wqp->word1.cn78xx.grp = xgrp; +		CVMX_SYNCWS; + +		tag_req.s_cn78xx_other.op = CVMX_POW_TAG_OP_ADDWQ; +		tag_req.s_cn78xx_other.type = tag_type; +		tag_req.s_cn78xx_other.wqp = cvmx_ptr_to_phys(wqp); +		tag_req.s_cn78xx_other.grp = xgrp; + +		ptr.s_cn78xx.did = 0x66; // CVMX_OCT_DID_TAG_TAG6; +		ptr.s_cn78xx.mem_region = CVMX_IO_SEG; +		ptr.s_cn78xx.is_io = 1; +		ptr.s_cn78xx.node = node; +		ptr.s_cn78xx.tag = tag; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		/* Reset all reserved bits */ +		wqp->word1.cn68xx.zero_0 = 0; +		wqp->word1.cn68xx.zero_1 = 0; +		wqp->word1.cn68xx.zero_2 = 0; +		wqp->word1.cn68xx.qos = qos; +		wqp->word1.cn68xx.grp = grp; + +		wqp->word1.tag = tag; +		wqp->word1.tag_type = tag_type; + +		tag_req.s_cn68xx_add.op = CVMX_POW_TAG_OP_ADDWQ; +		tag_req.s_cn68xx_add.type = tag_type; +		tag_req.s_cn68xx_add.tag = tag; +		tag_req.s_cn68xx_add.qos = qos; +		tag_req.s_cn68xx_add.grp = grp; + +		ptr.s.mem_region = CVMX_IO_SEG; +		ptr.s.is_io = 1; +		ptr.s.did = CVMX_OCT_DID_TAG_TAG1; +		ptr.s.addr = cvmx_ptr_to_phys(wqp); +	} else { +		/* Reset all reserved bits */ +		wqp->word1.cn38xx.zero_2 = 0; +		wqp->word1.cn38xx.qos = qos; +		wqp->word1.cn38xx.grp = grp; + +		wqp->word1.tag = tag; +		wqp->word1.tag_type = tag_type; + +		tag_req.s_cn38xx.op = CVMX_POW_TAG_OP_ADDWQ; +		tag_req.s_cn38xx.type = tag_type; +		tag_req.s_cn38xx.tag = tag; +		tag_req.s_cn38xx.qos = qos; +		tag_req.s_cn38xx.grp = grp; + +		ptr.s.mem_region = CVMX_IO_SEG; +		ptr.s.is_io = 1; +		ptr.s.did = CVMX_OCT_DID_TAG_TAG1; +		ptr.s.addr = cvmx_ptr_to_phys(wqp); +	} +	/* SYNC write to memory before the work submit. +	 * This is necessary as POW may read values from DRAM at this time */ +	CVMX_SYNCWS; +	cvmx_write_io(ptr.u64, tag_req.u64); +} + +/** + * This function sets the group mask for a core.  The group mask + * indicates which groups each core will accept work from. There are + * 16 groups. + * + * @param core_num   core to apply mask to + * @param mask   Group mask, one bit for up to 64 groups. + *               Each 1 bit in the mask enables the core to accept work from + *               the corresponding group. + *               The CN68XX supports 64 groups, earlier models only support + *               16 groups. + * + * The CN78XX in backwards compatibility mode allows up to 32 groups, + * so the 'mask' argument has one bit for every of the legacy + * groups, and a '1' in the mask causes a total of 8 groups + * which share the legacy group numbher and 8 qos levels, + * to be enabled for the calling processor core. + * A '0' in the mask will disable the current core + * from receiving work from the associated group. + */ +static inline void cvmx_pow_set_group_mask(u64 core_num, u64 mask) +{ +	u64 valid_mask; +	int num_groups = cvmx_pow_num_groups(); + +	if (num_groups >= 64) +		valid_mask = ~0ull; +	else +		valid_mask = (1ull << num_groups) - 1; + +	if ((mask & valid_mask) == 0) { +		printf("ERROR: %s empty group mask disables work on core# %llu, ignored.\n", +		       __func__, (unsigned long long)core_num); +		return; +	} +	cvmx_warn_if(mask & (~valid_mask), "%s group number range exceeded: %#llx\n", __func__, +		     (unsigned long long)mask); + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		unsigned int mask_set; +		cvmx_sso_ppx_sx_grpmskx_t grp_msk; +		unsigned int core, node; +		unsigned int rix;  /* Register index */ +		unsigned int grp;  /* Legacy group # */ +		unsigned int bit;  /* bit index */ +		unsigned int xgrp; /* native group # */ + +		node = cvmx_coremask_core_to_node(core_num); +		core = cvmx_coremask_core_on_node(core_num); + +		/* 78xx: 256 groups divided into 4 X 64 bit registers */ +		/* 73xx: 64 groups are in one register */ +		for (rix = 0; rix < (cvmx_sso_num_xgrp() >> 6); rix++) { +			grp_msk.u64 = 0; +			for (bit = 0; bit < 64; bit++) { +				/* 8-bit native XGRP number */ +				xgrp = (rix << 6) | bit; +				/* Legacy 5-bit group number */ +				grp = (xgrp >> 3) & 0x1f; +				/* Inspect legacy mask by legacy group */ +				if (mask & (1ull << grp)) +					grp_msk.s.grp_msk |= 1ull << bit; +				/* Pre-set to all 0's */ +			} +			for (mask_set = 0; mask_set < cvmx_sso_num_maskset(); mask_set++) { +				csr_wr_node(node, CVMX_SSO_PPX_SX_GRPMSKX(core, mask_set, rix), +					    grp_msk.u64); +			} +		} +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		cvmx_sso_ppx_grp_msk_t grp_msk; + +		grp_msk.s.grp_msk = mask; +		csr_wr(CVMX_SSO_PPX_GRP_MSK(core_num), grp_msk.u64); +	} else { +		cvmx_pow_pp_grp_mskx_t grp_msk; + +		grp_msk.u64 = csr_rd(CVMX_POW_PP_GRP_MSKX(core_num)); +		grp_msk.s.grp_msk = mask & 0xffff; +		csr_wr(CVMX_POW_PP_GRP_MSKX(core_num), grp_msk.u64); +	} +} + +/** + * This function gets the group mask for a core.  The group mask + * indicates which groups each core will accept work from. + * + * @param core_num   core to apply mask to + * @return	Group mask, one bit for up to 64 groups. + *               Each 1 bit in the mask enables the core to accept work from + *               the corresponding group. + *               The CN68XX supports 64 groups, earlier models only support + *               16 groups. + * + * The CN78XX in backwards compatibility mode allows up to 32 groups, + * so the 'mask' argument has one bit for every of the legacy + * groups, and a '1' in the mask causes a total of 8 groups + * which share the legacy group numbher and 8 qos levels, + * to be enabled for the calling processor core. + * A '0' in the mask will disable the current core + * from receiving work from the associated group. + */ +static inline u64 cvmx_pow_get_group_mask(u64 core_num) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_sso_ppx_sx_grpmskx_t grp_msk; +		unsigned int core, node, i; +		int rix; /* Register index */ +		u64 mask = 0; + +		node = cvmx_coremask_core_to_node(core_num); +		core = cvmx_coremask_core_on_node(core_num); + +		/* 78xx: 256 groups divided into 4 X 64 bit registers */ +		/* 73xx: 64 groups are in one register */ +		for (rix = (cvmx_sso_num_xgrp() >> 6) - 1; rix >= 0; rix--) { +			/* read only mask_set=0 (both 'set' was written same) */ +			grp_msk.u64 = csr_rd_node(node, CVMX_SSO_PPX_SX_GRPMSKX(core, 0, rix)); +			/* ASSUME: (this is how mask bits got written) */ +			/* grp_mask[7:0]: all bits 0..7 are same */ +			/* grp_mask[15:8]: all bits 8..15 are same, etc */ +			/* DO: mask[7:0] = grp_mask.u64[56,48,40,32,24,16,8,0] */ +			for (i = 0; i < 8; i++) +				mask |= (grp_msk.u64 & ((u64)1 << (i * 8))) >> (7 * i); +			/* we collected 8 MSBs in mask[7:0], <<=8 and continue */ +			if (cvmx_likely(rix != 0)) +				mask <<= 8; +		} +		return mask & 0xFFFFFFFF; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		cvmx_sso_ppx_grp_msk_t grp_msk; + +		grp_msk.u64 = csr_rd(CVMX_SSO_PPX_GRP_MSK(core_num)); +		return grp_msk.u64; +	} else { +		cvmx_pow_pp_grp_mskx_t grp_msk; + +		grp_msk.u64 = csr_rd(CVMX_POW_PP_GRP_MSKX(core_num)); +		return grp_msk.u64 & 0xffff; +	} +} + +/* + * Returns 0 if 78xx(73xx,75xx) is not programmed in legacy compatible mode + * Returns 1 if 78xx(73xx,75xx) is programmed in legacy compatible mode + * Returns 1 if octeon model is not 78xx(73xx,75xx) + */ +static inline u64 cvmx_pow_is_legacy78mode(u64 core_num) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_sso_ppx_sx_grpmskx_t grp_msk0, grp_msk1; +		unsigned int core, node, i; +		int rix; /* Register index */ +		u64 mask = 0; + +		node = cvmx_coremask_core_to_node(core_num); +		core = cvmx_coremask_core_on_node(core_num); + +		/* 78xx: 256 groups divided into 4 X 64 bit registers */ +		/* 73xx: 64 groups are in one register */ +		/* 1) in order for the 78_SSO to be in legacy compatible mode +		 * the both mask_sets should be programmed the same */ +		for (rix = (cvmx_sso_num_xgrp() >> 6) - 1; rix >= 0; rix--) { +			/* read mask_set=0 (both 'set' was written same) */ +			grp_msk0.u64 = csr_rd_node(node, CVMX_SSO_PPX_SX_GRPMSKX(core, 0, rix)); +			grp_msk1.u64 = csr_rd_node(node, CVMX_SSO_PPX_SX_GRPMSKX(core, 1, rix)); +			if (grp_msk0.u64 != grp_msk1.u64) { +				return 0; +			} +			/* (this is how mask bits should be written) */ +			/* grp_mask[7:0]: all bits 0..7 are same */ +			/* grp_mask[15:8]: all bits 8..15 are same, etc */ +			/* 2) in order for the 78_SSO to be in legacy compatible +			 * mode above should be true (test only mask_set=0 */ +			for (i = 0; i < 8; i++) { +				mask = (grp_msk0.u64 >> (i << 3)) & 0xFF; +				if (!(mask == 0 || mask == 0xFF)) { +					return 0; +				} +			} +		} +		/* if we come here, the 78_SSO is in legacy compatible mode */ +	} +	return 1; /* the SSO/POW is in legacy (or compatible) mode */ +} + +/** + * This function sets POW static priorities for a core. Each input queue has + * an associated priority value. + * + * @param core_num   core to apply priorities to + * @param priority   Vector of 8 priorities, one per POW Input Queue (0-7). + *                   Highest priority is 0 and lowest is 7. A priority value + *                   of 0xF instructs POW to skip the Input Queue when + *                   scheduling to this specific core. + *                   NOTE: priorities should not have gaps in values, meaning + *                         {0,1,1,1,1,1,1,1} is a valid configuration while + *                         {0,2,2,2,2,2,2,2} is not. + */ +static inline void cvmx_pow_set_priority(u64 core_num, const u8 priority[]) +{ +	/* Detect gaps between priorities and flag error */ +	if (!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		int i; +		u32 prio_mask = 0; + +		for (i = 0; i < 8; i++) +			if (priority[i] != 0xF) +				prio_mask |= 1 << priority[i]; + +		if (prio_mask ^ ((1 << cvmx_pop(prio_mask)) - 1)) { +			debug("ERROR: POW static priorities should be contiguous (0x%llx)\n", +			      (unsigned long long)prio_mask); +			return; +		} +	} + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		unsigned int group; +		unsigned int node = cvmx_get_node_num(); +		cvmx_sso_grpx_pri_t grp_pri; + +		/*grp_pri.s.weight = 0x3f; these will be anyway overwritten */ +		/*grp_pri.s.affinity = 0xf; by the next csr_rd_node(..), */ + +		for (group = 0; group < cvmx_sso_num_xgrp(); group++) { +			grp_pri.u64 = csr_rd_node(node, CVMX_SSO_GRPX_PRI(group)); +			grp_pri.s.pri = priority[group & 0x7]; +			csr_wr_node(node, CVMX_SSO_GRPX_PRI(group), grp_pri.u64); +		} + +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		cvmx_sso_ppx_qos_pri_t qos_pri; + +		qos_pri.u64 = csr_rd(CVMX_SSO_PPX_QOS_PRI(core_num)); +		qos_pri.s.qos0_pri = priority[0]; +		qos_pri.s.qos1_pri = priority[1]; +		qos_pri.s.qos2_pri = priority[2]; +		qos_pri.s.qos3_pri = priority[3]; +		qos_pri.s.qos4_pri = priority[4]; +		qos_pri.s.qos5_pri = priority[5]; +		qos_pri.s.qos6_pri = priority[6]; +		qos_pri.s.qos7_pri = priority[7]; +		csr_wr(CVMX_SSO_PPX_QOS_PRI(core_num), qos_pri.u64); +	} else { +		/* POW priorities on CN5xxx .. CN66XX */ +		cvmx_pow_pp_grp_mskx_t grp_msk; + +		grp_msk.u64 = csr_rd(CVMX_POW_PP_GRP_MSKX(core_num)); +		grp_msk.s.qos0_pri = priority[0]; +		grp_msk.s.qos1_pri = priority[1]; +		grp_msk.s.qos2_pri = priority[2]; +		grp_msk.s.qos3_pri = priority[3]; +		grp_msk.s.qos4_pri = priority[4]; +		grp_msk.s.qos5_pri = priority[5]; +		grp_msk.s.qos6_pri = priority[6]; +		grp_msk.s.qos7_pri = priority[7]; + +		csr_wr(CVMX_POW_PP_GRP_MSKX(core_num), grp_msk.u64); +	} +} + +/** + * This function gets POW static priorities for a core. Each input queue has + * an associated priority value. + * + * @param[in]  core_num core to get priorities for + * @param[out] priority Pointer to u8[] where to return priorities + *			Vector of 8 priorities, one per POW Input Queue (0-7). + *			Highest priority is 0 and lowest is 7. A priority value + *			of 0xF instructs POW to skip the Input Queue when + *			scheduling to this specific core. + *                   NOTE: priorities should not have gaps in values, meaning + *                         {0,1,1,1,1,1,1,1} is a valid configuration while + *                         {0,2,2,2,2,2,2,2} is not. + */ +static inline void cvmx_pow_get_priority(u64 core_num, u8 priority[]) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		unsigned int group; +		unsigned int node = cvmx_get_node_num(); +		cvmx_sso_grpx_pri_t grp_pri; + +		/* read priority only from the first 8 groups */ +		/* the next groups are programmed the same (periodicaly) */ +		for (group = 0; group < 8 /*cvmx_sso_num_xgrp() */; group++) { +			grp_pri.u64 = csr_rd_node(node, CVMX_SSO_GRPX_PRI(group)); +			priority[group /* & 0x7 */] = grp_pri.s.pri; +		} + +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		cvmx_sso_ppx_qos_pri_t qos_pri; + +		qos_pri.u64 = csr_rd(CVMX_SSO_PPX_QOS_PRI(core_num)); +		priority[0] = qos_pri.s.qos0_pri; +		priority[1] = qos_pri.s.qos1_pri; +		priority[2] = qos_pri.s.qos2_pri; +		priority[3] = qos_pri.s.qos3_pri; +		priority[4] = qos_pri.s.qos4_pri; +		priority[5] = qos_pri.s.qos5_pri; +		priority[6] = qos_pri.s.qos6_pri; +		priority[7] = qos_pri.s.qos7_pri; +	} else { +		/* POW priorities on CN5xxx .. CN66XX */ +		cvmx_pow_pp_grp_mskx_t grp_msk; + +		grp_msk.u64 = csr_rd(CVMX_POW_PP_GRP_MSKX(core_num)); +		priority[0] = grp_msk.s.qos0_pri; +		priority[1] = grp_msk.s.qos1_pri; +		priority[2] = grp_msk.s.qos2_pri; +		priority[3] = grp_msk.s.qos3_pri; +		priority[4] = grp_msk.s.qos4_pri; +		priority[5] = grp_msk.s.qos5_pri; +		priority[6] = grp_msk.s.qos6_pri; +		priority[7] = grp_msk.s.qos7_pri; +	} + +	/* Detect gaps between priorities and flag error - (optional) */ +	if (!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		int i; +		u32 prio_mask = 0; + +		for (i = 0; i < 8; i++) +			if (priority[i] != 0xF) +				prio_mask |= 1 << priority[i]; + +		if (prio_mask ^ ((1 << cvmx_pop(prio_mask)) - 1)) { +			debug("ERROR:%s: POW static priorities should be contiguous (0x%llx)\n", +			      __func__, (unsigned long long)prio_mask); +			return; +		} +	} +} + +static inline void cvmx_sso_get_group_priority(int node, cvmx_xgrp_t xgrp, int *priority, +					       int *weight, int *affinity) +{ +	cvmx_sso_grpx_pri_t grp_pri; + +	if (!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		debug("ERROR: %s is not supported on this chip)\n", __func__); +		return; +	} + +	grp_pri.u64 = csr_rd_node(node, CVMX_SSO_GRPX_PRI(xgrp.xgrp)); +	*affinity = grp_pri.s.affinity; +	*priority = grp_pri.s.pri; +	*weight = grp_pri.s.weight; +} + +/** + * Performs a tag switch and then an immediate deschedule. This completes + * immediately, so completion must not be waited for.  This function does NOT + * update the wqe in DRAM to match arguments. + * + * This function does NOT wait for any prior tag switches to complete, so the + * calling code must do this. + * + * Note the following CAVEAT of the Octeon HW behavior when + * re-scheduling DE-SCHEDULEd items whose (next) state is + * ORDERED: + *   - If there are no switches pending at the time that the + *     HW executes the de-schedule, the HW will only re-schedule + *     the head of the FIFO associated with the given tag. This + *     means that in many respects, the HW treats this ORDERED + *     tag as an ATOMIC tag. Note that in the SWTAG_DESCH + *     case (to an ORDERED tag), the HW will do the switch + *     before the deschedule whenever it is possible to do + *     the switch immediately, so it may often look like + *     this case. + *   - If there is a pending switch to ORDERED at the time + *     the HW executes the de-schedule, the HW will perform + *     the switch at the time it re-schedules, and will be + *     able to reschedule any/all of the entries with the + *     same tag. + * Due to this behavior, the RECOMMENDATION to software is + * that they have a (next) state of ATOMIC when they + * DE-SCHEDULE. If an ORDERED tag is what was really desired, + * SW can choose to immediately switch to an ORDERED tag + * after the work (that has an ATOMIC tag) is re-scheduled. + * Note that since there are never any tag switches pending + * when the HW re-schedules, this switch can be IMMEDIATE upon + * the reception of the pointer during the re-schedule. + * + * @param tag      New tag value + * @param tag_type New tag type + * @param group    New group value + * @param no_sched Control whether this work queue entry will be rescheduled. + *                 - 1 : don't schedule this work + *                 - 0 : allow this work to be scheduled. + */ +static inline void cvmx_pow_tag_sw_desched_nocheck(u32 tag, cvmx_pow_tag_type_t tag_type, u64 group, +						   u64 no_sched) +{ +	union cvmx_pow_tag_req_addr ptr; +	cvmx_pow_tag_req_t tag_req; + +	if (CVMX_ENABLE_POW_CHECKS) { +		cvmx_pow_tag_info_t current_tag; + +		__cvmx_pow_warn_if_pending_switch(__func__); +		current_tag = cvmx_pow_get_current_tag(); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL_NULL, +			     "%s called with NULL_NULL tag\n", __func__); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL, +			     "%s called with NULL tag. Deschedule not allowed from NULL state\n", +			     __func__); +		cvmx_warn_if((current_tag.tag_type != CVMX_POW_TAG_TYPE_ATOMIC) && +			     (tag_type != CVMX_POW_TAG_TYPE_ATOMIC), +			     "%s called where neither the before or after tag is ATOMIC\n", +			     __func__); +	} +	tag_req.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_t *wqp = cvmx_pow_get_current_wqp(); + +		if (!wqp) { +			debug("ERROR: Failed to get WQE, %s\n", __func__); +			return; +		} +		group &= 0x1f; +		wqp->word1.cn78xx.tag = tag; +		wqp->word1.cn78xx.tag_type = tag_type; +		wqp->word1.cn78xx.grp = group << 3; +		CVMX_SYNCWS; +		tag_req.s_cn78xx_other.op = CVMX_POW_TAG_OP_SWTAG_DESCH; +		tag_req.s_cn78xx_other.type = tag_type; +		tag_req.s_cn78xx_other.grp = group << 3; +		tag_req.s_cn78xx_other.no_sched = no_sched; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		group &= 0x3f; +		tag_req.s_cn68xx_other.op = CVMX_POW_TAG_OP_SWTAG_DESCH; +		tag_req.s_cn68xx_other.tag = tag; +		tag_req.s_cn68xx_other.type = tag_type; +		tag_req.s_cn68xx_other.grp = group; +		tag_req.s_cn68xx_other.no_sched = no_sched; +	} else { +		group &= 0x0f; +		tag_req.s_cn38xx.op = CVMX_POW_TAG_OP_SWTAG_DESCH; +		tag_req.s_cn38xx.tag = tag; +		tag_req.s_cn38xx.type = tag_type; +		tag_req.s_cn38xx.grp = group; +		tag_req.s_cn38xx.no_sched = no_sched; +	} +	ptr.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		ptr.s.mem_region = CVMX_IO_SEG; +		ptr.s.is_io = 1; +		ptr.s.did = CVMX_OCT_DID_TAG_TAG3; +		ptr.s_cn78xx.node = cvmx_get_node_num(); +		ptr.s_cn78xx.tag = tag; +	} else { +		ptr.s.mem_region = CVMX_IO_SEG; +		ptr.s.is_io = 1; +		ptr.s.did = CVMX_OCT_DID_TAG_TAG3; +	} +	cvmx_write_io(ptr.u64, tag_req.u64); +} + +/** + * Performs a tag switch and then an immediate deschedule. This completes + * immediately, so completion must not be waited for.  This function does NOT + * update the wqe in DRAM to match arguments. + * + * This function waits for any prior tag switches to complete, so the + * calling code may call this function with a pending tag switch. + * + * Note the following CAVEAT of the Octeon HW behavior when + * re-scheduling DE-SCHEDULEd items whose (next) state is + * ORDERED: + *   - If there are no switches pending at the time that the + *     HW executes the de-schedule, the HW will only re-schedule + *     the head of the FIFO associated with the given tag. This + *     means that in many respects, the HW treats this ORDERED + *     tag as an ATOMIC tag. Note that in the SWTAG_DESCH + *     case (to an ORDERED tag), the HW will do the switch + *     before the deschedule whenever it is possible to do + *     the switch immediately, so it may often look like + *     this case. + *   - If there is a pending switch to ORDERED at the time + *     the HW executes the de-schedule, the HW will perform + *     the switch at the time it re-schedules, and will be + *     able to reschedule any/all of the entries with the + *     same tag. + * Due to this behavior, the RECOMMENDATION to software is + * that they have a (next) state of ATOMIC when they + * DE-SCHEDULE. If an ORDERED tag is what was really desired, + * SW can choose to immediately switch to an ORDERED tag + * after the work (that has an ATOMIC tag) is re-scheduled. + * Note that since there are never any tag switches pending + * when the HW re-schedules, this switch can be IMMEDIATE upon + * the reception of the pointer during the re-schedule. + * + * @param tag      New tag value + * @param tag_type New tag type + * @param group    New group value + * @param no_sched Control whether this work queue entry will be rescheduled. + *                 - 1 : don't schedule this work + *                 - 0 : allow this work to be scheduled. + */ +static inline void cvmx_pow_tag_sw_desched(u32 tag, cvmx_pow_tag_type_t tag_type, u64 group, +					   u64 no_sched) +{ +	/* Need to make sure any writes to the work queue entry are complete */ +	CVMX_SYNCWS; +	/* Ensure that there is not a pending tag switch, as a tag switch cannot be started +	 * if a previous switch is still pending.  */ +	cvmx_pow_tag_sw_wait(); +	cvmx_pow_tag_sw_desched_nocheck(tag, tag_type, group, no_sched); +} + +/** + * Descchedules the current work queue entry. + * + * @param no_sched no schedule flag value to be set on the work queue entry. + *     If this is set the entry will not be rescheduled. + */ +static inline void cvmx_pow_desched(u64 no_sched) +{ +	union cvmx_pow_tag_req_addr ptr; +	cvmx_pow_tag_req_t tag_req; + +	if (CVMX_ENABLE_POW_CHECKS) { +		cvmx_pow_tag_info_t current_tag; + +		__cvmx_pow_warn_if_pending_switch(__func__); +		current_tag = cvmx_pow_get_current_tag(); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL_NULL, +			     "%s called with NULL_NULL tag\n", __func__); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL, +			     "%s called with NULL tag. Deschedule not expected from NULL state\n", +			     __func__); +	} +	/* Need to make sure any writes to the work queue entry are complete */ +	CVMX_SYNCWS; + +	tag_req.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		tag_req.s_cn78xx_other.op = CVMX_POW_TAG_OP_DESCH; +		tag_req.s_cn78xx_other.no_sched = no_sched; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		tag_req.s_cn68xx_other.op = CVMX_POW_TAG_OP_DESCH; +		tag_req.s_cn68xx_other.no_sched = no_sched; +	} else { +		tag_req.s_cn38xx.op = CVMX_POW_TAG_OP_DESCH; +		tag_req.s_cn38xx.no_sched = no_sched; +	} +	ptr.u64 = 0; +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		ptr.s_cn78xx.mem_region = CVMX_IO_SEG; +		ptr.s_cn78xx.is_io = 1; +		ptr.s_cn78xx.did = CVMX_OCT_DID_TAG_TAG3; +		ptr.s_cn78xx.node = cvmx_get_node_num(); +	} else { +		ptr.s.mem_region = CVMX_IO_SEG; +		ptr.s.is_io = 1; +		ptr.s.did = CVMX_OCT_DID_TAG_TAG3; +	} +	cvmx_write_io(ptr.u64, tag_req.u64); +} + +/******************************************************************************/ +/* OCTEON3-specific functions.                                                */ +/******************************************************************************/ +/** + * This function sets the the affinity of group to the cores in 78xx. + * It sets up all the cores in core_mask to accept work from the specified group. + * + * @param xgrp	Group to accept work from, 0 - 255. + * @param core_mask	Mask of all the cores which will accept work from this group + * @param mask_set	Every core has set of 2 masks which can be set to accept work + *     from 256 groups. At the time of get_work, cores can choose which mask_set + *     to get work from. 'mask_set' values range from 0 to 3, where	each of the + *     two bits represents a mask set. Cores will be added to the mask set with + *     corresponding bit set, and removed from the mask set with corresponding + *     bit clear. + * Note: cores can only accept work from SSO groups on the same node, + * so the node number for the group is derived from the core number. + */ +static inline void cvmx_sso_set_group_core_affinity(cvmx_xgrp_t xgrp, +						    const struct cvmx_coremask *core_mask, +						    u8 mask_set) +{ +	cvmx_sso_ppx_sx_grpmskx_t grp_msk; +	int core; +	int grp_index = xgrp.xgrp >> 6; +	int bit_pos = xgrp.xgrp % 64; + +	if (!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		debug("ERROR: %s is not supported on this chip)\n", __func__); +		return; +	} +	cvmx_coremask_for_each_core(core, core_mask) +	{ +		unsigned int node, ncore; +		u64 reg_addr; + +		node = cvmx_coremask_core_to_node(core); +		ncore = cvmx_coremask_core_on_node(core); + +		reg_addr = CVMX_SSO_PPX_SX_GRPMSKX(ncore, 0, grp_index); +		grp_msk.u64 = csr_rd_node(node, reg_addr); + +		if (mask_set & 1) +			grp_msk.s.grp_msk |= (1ull << bit_pos); +		else +			grp_msk.s.grp_msk &= ~(1ull << bit_pos); + +		csr_wr_node(node, reg_addr, grp_msk.u64); + +		reg_addr = CVMX_SSO_PPX_SX_GRPMSKX(ncore, 1, grp_index); +		grp_msk.u64 = csr_rd_node(node, reg_addr); + +		if (mask_set & 2) +			grp_msk.s.grp_msk |= (1ull << bit_pos); +		else +			grp_msk.s.grp_msk &= ~(1ull << bit_pos); + +		csr_wr_node(node, reg_addr, grp_msk.u64); +	} +} + +/** + * This function sets the priority and group affinity arbitration for each group. + * + * @param node		Node number + * @param xgrp	Group 0 - 255 to apply mask parameters to + * @param priority	Priority of the group relative to other groups + *     0x0 - highest priority + *     0x7 - lowest priority + * @param weight	Cross-group arbitration weight to apply to this group. + *     valid values are 1-63 + *     h/w default is 0x3f + * @param affinity	Processor affinity arbitration weight to apply to this group. + *     If zero, affinity is disabled. + *     valid values are 0-15 + *     h/w default which is 0xf. + * @param modify_mask   mask of the parameters which needs to be modified. + *     enum cvmx_sso_group_modify_mask + *     to modify only priority -- set bit0 + *     to modify only weight   -- set bit1 + *     to modify only affinity -- set bit2 + */ +static inline void cvmx_sso_set_group_priority(int node, cvmx_xgrp_t xgrp, int priority, int weight, +					       int affinity, +					       enum cvmx_sso_group_modify_mask modify_mask) +{ +	cvmx_sso_grpx_pri_t grp_pri; + +	if (!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		debug("ERROR: %s is not supported on this chip)\n", __func__); +		return; +	} +	if (weight <= 0) +		weight = 0x3f; /* Force HW default when out of range */ + +	grp_pri.u64 = csr_rd_node(node, CVMX_SSO_GRPX_PRI(xgrp.xgrp)); +	if (grp_pri.s.weight == 0) +		grp_pri.s.weight = 0x3f; +	if (modify_mask & CVMX_SSO_MODIFY_GROUP_PRIORITY) +		grp_pri.s.pri = priority; +	if (modify_mask & CVMX_SSO_MODIFY_GROUP_WEIGHT) +		grp_pri.s.weight = weight; +	if (modify_mask & CVMX_SSO_MODIFY_GROUP_AFFINITY) +		grp_pri.s.affinity = affinity; +	csr_wr_node(node, CVMX_SSO_GRPX_PRI(xgrp.xgrp), grp_pri.u64); +} + +/** + * Asynchronous work request. + * Only works on CN78XX style SSO. + * + * Work is requested from the SSO unit, and should later be checked with + * function cvmx_pow_work_response_async. + * This function does NOT wait for previous tag switches to complete, + * so the caller must ensure that there is not a pending tag switch. + * + * @param scr_addr Scratch memory address that response will be returned to, + *     which is either a valid WQE, or a response with the invalid bit set. + *     Byte address, must be 8 byte aligned. + * @param xgrp  Group to receive work for (0-255). + * @param wait + *     1 to cause response to wait for work to become available (or timeout) + *     0 to cause response to return immediately + */ +static inline void cvmx_sso_work_request_grp_async_nocheck(int scr_addr, cvmx_xgrp_t xgrp, +							   cvmx_pow_wait_t wait) +{ +	cvmx_pow_iobdma_store_t data; +	unsigned int node = cvmx_get_node_num(); + +	if (CVMX_ENABLE_POW_CHECKS) { +		__cvmx_pow_warn_if_pending_switch(__func__); +		cvmx_warn_if(!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE), "Not CN78XX"); +	} +	/* scr_addr must be 8 byte aligned */ +	data.u64 = 0; +	data.s_cn78xx.scraddr = scr_addr >> 3; +	data.s_cn78xx.len = 1; +	data.s_cn78xx.did = CVMX_OCT_DID_TAG_SWTAG; +	data.s_cn78xx.grouped = 1; +	data.s_cn78xx.index_grp_mask = (node << 8) | xgrp.xgrp; +	data.s_cn78xx.wait = wait; +	data.s_cn78xx.node = node; + +	cvmx_send_single(data.u64); +} + +/** + * Synchronous work request from the node-local SSO without verifying + * pending tag switch. It requests work from a specific SSO group. + * + * @param lgrp The local group number (within the SSO of the node of the caller) + *     from which to get the work. + * @param wait When set, call stalls until work becomes available, or times out. + *     If not set, returns immediately. + * + * @return Returns the WQE pointer from SSO. + *     Returns NULL if no work was available. + */ +static inline void *cvmx_sso_work_request_grp_sync_nocheck(unsigned int lgrp, cvmx_pow_wait_t wait) +{ +	cvmx_pow_load_addr_t ptr; +	cvmx_pow_tag_load_resp_t result; +	unsigned int node = cvmx_get_node_num() & 3; + +	if (CVMX_ENABLE_POW_CHECKS) { +		__cvmx_pow_warn_if_pending_switch(__func__); +		cvmx_warn_if(!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE), "Not CN78XX"); +	} +	ptr.u64 = 0; +	ptr.swork_78xx.mem_region = CVMX_IO_SEG; +	ptr.swork_78xx.is_io = 1; +	ptr.swork_78xx.did = CVMX_OCT_DID_TAG_SWTAG; +	ptr.swork_78xx.node = node; +	ptr.swork_78xx.grouped = 1; +	ptr.swork_78xx.index = (lgrp & 0xff) | node << 8; +	ptr.swork_78xx.wait = wait; + +	result.u64 = csr_rd(ptr.u64); +	if (result.s_work.no_work) +		return NULL; +	else +		return cvmx_phys_to_ptr(result.s_work.addr); +} + +/** + * Synchronous work request from the node-local SSO. + * It requests work from a specific SSO group. + * This function waits for any previous tag switch to complete before + * requesting the new work. + * + * @param lgrp The node-local group number from which to get the work. + * @param wait When set, call stalls until work becomes available, or times out. + *     If not set, returns immediately. + * + * @return The WQE pointer or NULL, if work is not available. + */ +static inline void *cvmx_sso_work_request_grp_sync(unsigned int lgrp, cvmx_pow_wait_t wait) +{ +	cvmx_pow_tag_sw_wait(); +	return cvmx_sso_work_request_grp_sync_nocheck(lgrp, wait); +} + +/** + * This function sets the group mask for a core.  The group mask bits + * indicate which groups each core will accept work from. + * + * @param core_num	Processor core to apply mask to. + * @param mask_set	7XXX has 2 sets of masks per core. + *     Bit 0 represents the first mask set, bit 1 -- the second. + * @param xgrp_mask	Group mask array. + *     Total number of groups is divided into a number of + *     64-bits mask sets. Each bit in the mask, if set, enables + *     the core to accept work from the corresponding group. + * + * NOTE: Each core can be configured to accept work in accordance to both + * mask sets, with the first having higher precedence over the second, + * or to accept work in accordance to just one of the two mask sets. + * The 'core_num' argument represents a processor core on any node + * in a coherent multi-chip system. + * + * If the 'mask_set' argument is 3, both mask sets are configured + * with the same value (which is not typically the intention), + * so keep in mind the function needs to be called twice + * to set a different value into each of the mask sets, + * once with 'mask_set=1' and second time with 'mask_set=2'. + */ +static inline void cvmx_pow_set_xgrp_mask(u64 core_num, u8 mask_set, const u64 xgrp_mask[]) +{ +	unsigned int grp, node, core; +	u64 reg_addr; + +	if (!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		debug("ERROR: %s is not supported on this chip)\n", __func__); +		return; +	} + +	if (CVMX_ENABLE_POW_CHECKS) { +		cvmx_warn_if(((mask_set < 1) || (mask_set > 3)), "Invalid mask set"); +	} + +	if ((mask_set < 1) || (mask_set > 3)) +		mask_set = 3; + +	node = cvmx_coremask_core_to_node(core_num); +	core = cvmx_coremask_core_on_node(core_num); + +	for (grp = 0; grp < (cvmx_sso_num_xgrp() >> 6); grp++) { +		if (mask_set & 1) { +			reg_addr = CVMX_SSO_PPX_SX_GRPMSKX(core, 0, grp), +			csr_wr_node(node, reg_addr, xgrp_mask[grp]); +		} +		if (mask_set & 2) { +			reg_addr = CVMX_SSO_PPX_SX_GRPMSKX(core, 1, grp), +			csr_wr_node(node, reg_addr, xgrp_mask[grp]); +		} +	} +} + +/** + * This function gets the group mask for a core.  The group mask bits + * indicate which groups each core will accept work from. + * + * @param core_num	Processor core to apply mask to. + * @param mask_set	7XXX has 2 sets of masks per core. + *     Bit 0 represents the first mask set, bit 1 -- the second. + * @param xgrp_mask	Provide pointer to u64 mask[8] output array. + *     Total number of groups is divided into a number of + *     64-bits mask sets. Each bit in the mask represents + *     the core accepts work from the corresponding group. + * + * NOTE: Each core can be configured to accept work in accordance to both + * mask sets, with the first having higher precedence over the second, + * or to accept work in accordance to just one of the two mask sets. + * The 'core_num' argument represents a processor core on any node + * in a coherent multi-chip system. + */ +static inline void cvmx_pow_get_xgrp_mask(u64 core_num, u8 mask_set, u64 *xgrp_mask) +{ +	cvmx_sso_ppx_sx_grpmskx_t grp_msk; +	unsigned int grp, node, core; +	u64 reg_addr; + +	if (!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		debug("ERROR: %s is not supported on this chip)\n", __func__); +		return; +	} + +	if (CVMX_ENABLE_POW_CHECKS) { +		cvmx_warn_if(mask_set != 1 && mask_set != 2, "Invalid mask set"); +	} + +	node = cvmx_coremask_core_to_node(core_num); +	core = cvmx_coremask_core_on_node(core_num); + +	for (grp = 0; grp < cvmx_sso_num_xgrp() >> 6; grp++) { +		if (mask_set & 1) { +			reg_addr = CVMX_SSO_PPX_SX_GRPMSKX(core, 0, grp), +			grp_msk.u64 = csr_rd_node(node, reg_addr); +			xgrp_mask[grp] = grp_msk.s.grp_msk; +		} +		if (mask_set & 2) { +			reg_addr = CVMX_SSO_PPX_SX_GRPMSKX(core, 1, grp), +			grp_msk.u64 = csr_rd_node(node, reg_addr); +			xgrp_mask[grp] = grp_msk.s.grp_msk; +		} +	} +} + +/** + * Executes SSO SWTAG command. + * This is similar to cvmx_pow_tag_sw() function, but uses linear + * (vs. integrated group-qos) group index. + */ +static inline void cvmx_pow_tag_sw_node(cvmx_wqe_t *wqp, u32 tag, cvmx_pow_tag_type_t tag_type, +					int node) +{ +	union cvmx_pow_tag_req_addr ptr; +	cvmx_pow_tag_req_t tag_req; + +	if (cvmx_unlikely(!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE))) { +		debug("ERROR: %s is supported on OCTEON3 only\n", __func__); +		return; +	} +	CVMX_SYNCWS; +	cvmx_pow_tag_sw_wait(); + +	if (CVMX_ENABLE_POW_CHECKS) { +		cvmx_pow_tag_info_t current_tag; + +		__cvmx_pow_warn_if_pending_switch(__func__); +		current_tag = cvmx_pow_get_current_tag(); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL_NULL, +			     "%s called with NULL_NULL tag\n", __func__); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL, +			     "%s called with NULL tag\n", __func__); +		cvmx_warn_if((current_tag.tag_type == tag_type) && (current_tag.tag == tag), +			     "%s called to perform a tag switch to the same tag\n", __func__); +		cvmx_warn_if( +			tag_type == CVMX_POW_TAG_TYPE_NULL, +			"%s called to perform a tag switch to NULL. Use cvmx_pow_tag_sw_null() instead\n", +			__func__); +	} +	wqp->word1.cn78xx.tag = tag; +	wqp->word1.cn78xx.tag_type = tag_type; +	CVMX_SYNCWS; + +	tag_req.u64 = 0; +	tag_req.s_cn78xx_other.op = CVMX_POW_TAG_OP_SWTAG; +	tag_req.s_cn78xx_other.type = tag_type; + +	ptr.u64 = 0; +	ptr.s_cn78xx.mem_region = CVMX_IO_SEG; +	ptr.s_cn78xx.is_io = 1; +	ptr.s_cn78xx.did = CVMX_OCT_DID_TAG_SWTAG; +	ptr.s_cn78xx.node = node; +	ptr.s_cn78xx.tag = tag; +	cvmx_write_io(ptr.u64, tag_req.u64); +} + +/** + * Executes SSO SWTAG_FULL command. + * This is similar to cvmx_pow_tag_sw_full() function, but + * uses linear (vs. integrated group-qos) group index. + */ +static inline void cvmx_pow_tag_sw_full_node(cvmx_wqe_t *wqp, u32 tag, cvmx_pow_tag_type_t tag_type, +					     u8 xgrp, int node) +{ +	union cvmx_pow_tag_req_addr ptr; +	cvmx_pow_tag_req_t tag_req; +	u16 gxgrp; + +	if (cvmx_unlikely(!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE))) { +		debug("ERROR: %s is supported on OCTEON3 only\n", __func__); +		return; +	} +	/* Ensure that there is not a pending tag switch, as a tag switch cannot be +	 * started, if a previous switch is still pending. */ +	CVMX_SYNCWS; +	cvmx_pow_tag_sw_wait(); + +	if (CVMX_ENABLE_POW_CHECKS) { +		cvmx_pow_tag_info_t current_tag; + +		__cvmx_pow_warn_if_pending_switch(__func__); +		current_tag = cvmx_pow_get_current_tag(); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL_NULL, +			     "%s called with NULL_NULL tag\n", __func__); +		cvmx_warn_if((current_tag.tag_type == tag_type) && (current_tag.tag == tag), +			     "%s called to perform a tag switch to the same tag\n", __func__); +		cvmx_warn_if( +			tag_type == CVMX_POW_TAG_TYPE_NULL, +			"%s called to perform a tag switch to NULL. Use cvmx_pow_tag_sw_null() instead\n", +			__func__); +		if ((wqp != cvmx_phys_to_ptr(0x80)) && cvmx_pow_get_current_wqp()) +			cvmx_warn_if(wqp != cvmx_pow_get_current_wqp(), +				     "%s passed WQE(%p) doesn't match the address in the POW(%p)\n", +				     __func__, wqp, cvmx_pow_get_current_wqp()); +	} +	gxgrp = node; +	gxgrp = gxgrp << 8 | xgrp; +	wqp->word1.cn78xx.grp = gxgrp; +	wqp->word1.cn78xx.tag = tag; +	wqp->word1.cn78xx.tag_type = tag_type; +	CVMX_SYNCWS; + +	tag_req.u64 = 0; +	tag_req.s_cn78xx_other.op = CVMX_POW_TAG_OP_SWTAG_FULL; +	tag_req.s_cn78xx_other.type = tag_type; +	tag_req.s_cn78xx_other.grp = gxgrp; +	tag_req.s_cn78xx_other.wqp = cvmx_ptr_to_phys(wqp); + +	ptr.u64 = 0; +	ptr.s_cn78xx.mem_region = CVMX_IO_SEG; +	ptr.s_cn78xx.is_io = 1; +	ptr.s_cn78xx.did = CVMX_OCT_DID_TAG_SWTAG; +	ptr.s_cn78xx.node = node; +	ptr.s_cn78xx.tag = tag; +	cvmx_write_io(ptr.u64, tag_req.u64); +} + +/** + * Submits work to an SSO group on any OCI node. + * This function updates the work queue entry in DRAM to match + * the arguments given. + * Note that the tag provided is for the work queue entry submitted, + * and is unrelated to the tag that the core currently holds. + * + * @param wqp pointer to work queue entry to submit. + * This entry is updated to match the other parameters + * @param tag tag value to be assigned to work queue entry + * @param tag_type type of tag + * @param xgrp native CN78XX group in the range 0..255 + * @param node The OCI node number for the target group + * + * When this function is called on a model prior to CN78XX, which does + * not support OCI nodes, the 'node' argument is ignored, and the 'xgrp' + * parameter is converted into 'qos' (the lower 3 bits) and 'grp' (the higher + * 5 bits), following the backward-compatibility scheme of translating + * between new and old style group numbers. + */ +static inline void cvmx_pow_work_submit_node(cvmx_wqe_t *wqp, u32 tag, cvmx_pow_tag_type_t tag_type, +					     u8 xgrp, u8 node) +{ +	union cvmx_pow_tag_req_addr ptr; +	cvmx_pow_tag_req_t tag_req; +	u16 group; + +	if (cvmx_unlikely(!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE))) { +		debug("ERROR: %s is supported on OCTEON3 only\n", __func__); +		return; +	} +	group = node; +	group = group << 8 | xgrp; +	wqp->word1.cn78xx.tag = tag; +	wqp->word1.cn78xx.tag_type = tag_type; +	wqp->word1.cn78xx.grp = group; +	CVMX_SYNCWS; + +	tag_req.u64 = 0; +	tag_req.s_cn78xx_other.op = CVMX_POW_TAG_OP_ADDWQ; +	tag_req.s_cn78xx_other.type = tag_type; +	tag_req.s_cn78xx_other.wqp = cvmx_ptr_to_phys(wqp); +	tag_req.s_cn78xx_other.grp = group; + +	ptr.u64 = 0; +	ptr.s_cn78xx.did = 0x66; // CVMX_OCT_DID_TAG_TAG6; +	ptr.s_cn78xx.mem_region = CVMX_IO_SEG; +	ptr.s_cn78xx.is_io = 1; +	ptr.s_cn78xx.node = node; +	ptr.s_cn78xx.tag = tag; + +	/* SYNC write to memory before the work submit.  This is necessary +	 ** as POW may read values from DRAM at this time */ +	CVMX_SYNCWS; +	cvmx_write_io(ptr.u64, tag_req.u64); +} + +/** + * Executes the SSO SWTAG_DESCHED operation. + * This is similar to the cvmx_pow_tag_sw_desched() function, but + * uses linear (vs. unified group-qos) group index. + */ +static inline void cvmx_pow_tag_sw_desched_node(cvmx_wqe_t *wqe, u32 tag, +						cvmx_pow_tag_type_t tag_type, u8 xgrp, u64 no_sched, +						u8 node) +{ +	union cvmx_pow_tag_req_addr ptr; +	cvmx_pow_tag_req_t tag_req; +	u16 group; + +	if (cvmx_unlikely(!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE))) { +		debug("ERROR: %s is supported on OCTEON3 only\n", __func__); +		return; +	} +	/* Need to make sure any writes to the work queue entry are complete */ +	CVMX_SYNCWS; +	/* +	 * Ensure that there is not a pending tag switch, as a tag switch cannot +	 * be started if a previous switch is still pending. +	 */ +	cvmx_pow_tag_sw_wait(); + +	if (CVMX_ENABLE_POW_CHECKS) { +		cvmx_pow_tag_info_t current_tag; + +		__cvmx_pow_warn_if_pending_switch(__func__); +		current_tag = cvmx_pow_get_current_tag(); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL_NULL, +			     "%s called with NULL_NULL tag\n", __func__); +		cvmx_warn_if(current_tag.tag_type == CVMX_POW_TAG_TYPE_NULL, +			     "%s called with NULL tag. Deschedule not allowed from NULL state\n", +			     __func__); +		cvmx_warn_if((current_tag.tag_type != CVMX_POW_TAG_TYPE_ATOMIC) && +			     (tag_type != CVMX_POW_TAG_TYPE_ATOMIC), +			     "%s called where neither the before or after tag is ATOMIC\n", +			     __func__); +	} +	group = node; +	group = group << 8 | xgrp; +	wqe->word1.cn78xx.tag = tag; +	wqe->word1.cn78xx.tag_type = tag_type; +	wqe->word1.cn78xx.grp = group; +	CVMX_SYNCWS; + +	tag_req.u64 = 0; +	tag_req.s_cn78xx_other.op = CVMX_POW_TAG_OP_SWTAG_DESCH; +	tag_req.s_cn78xx_other.type = tag_type; +	tag_req.s_cn78xx_other.grp = group; +	tag_req.s_cn78xx_other.no_sched = no_sched; + +	ptr.u64 = 0; +	ptr.s.mem_region = CVMX_IO_SEG; +	ptr.s.is_io = 1; +	ptr.s.did = CVMX_OCT_DID_TAG_TAG3; +	ptr.s_cn78xx.node = node; +	ptr.s_cn78xx.tag = tag; +	cvmx_write_io(ptr.u64, tag_req.u64); +} + +/* Executes the UPD_WQP_GRP SSO operation. + * + * @param wqp  Pointer to the new work queue entry to switch to. + * @param xgrp SSO group in the range 0..255 + * + * NOTE: The operation can be performed only on the local node. + */ +static inline void cvmx_sso_update_wqp_group(cvmx_wqe_t *wqp, u8 xgrp) +{ +	union cvmx_pow_tag_req_addr addr; +	cvmx_pow_tag_req_t data; +	int node = cvmx_get_node_num(); +	int group = node << 8 | xgrp; + +	if (!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		debug("ERROR: %s is not supported on this chip)\n", __func__); +		return; +	} +	wqp->word1.cn78xx.grp = group; +	CVMX_SYNCWS; + +	data.u64 = 0; +	data.s_cn78xx_other.op = CVMX_POW_TAG_OP_UPDATE_WQP_GRP; +	data.s_cn78xx_other.grp = group; +	data.s_cn78xx_other.wqp = cvmx_ptr_to_phys(wqp); + +	addr.u64 = 0; +	addr.s_cn78xx.mem_region = CVMX_IO_SEG; +	addr.s_cn78xx.is_io = 1; +	addr.s_cn78xx.did = CVMX_OCT_DID_TAG_TAG1; +	addr.s_cn78xx.node = node; +	cvmx_write_io(addr.u64, data.u64); +} + +/******************************************************************************/ +/* Define usage of bits within the 32 bit tag values.                         */ +/******************************************************************************/ +/* + * Number of bits of the tag used by software.  The SW bits + * are always a contiguous block of the high starting at bit 31. + * The hardware bits are always the low bits.  By default, the top 8 bits + * of the tag are reserved for software, and the low 24 are set by the IPD unit. + */ +#define CVMX_TAG_SW_BITS  (8) +#define CVMX_TAG_SW_SHIFT (32 - CVMX_TAG_SW_BITS) + +/* Below is the list of values for the top 8 bits of the tag. */ +/* + * Tag values with top byte of this value are reserved for internal executive + * uses + */ +#define CVMX_TAG_SW_BITS_INTERNAL 0x1 + +/* + * The executive divides the remaining 24 bits as follows: + * the upper 8 bits (bits 23 - 16 of the tag) define a subgroup + * the lower 16 bits (bits 15 - 0 of the tag) define are the value with + * the subgroup. Note that this section describes the format of tags generated + * by software - refer to the hardware documentation for a description of the + * tags values generated by the packet input hardware. + * Subgroups are defined here + */ + +/* Mask for the value portion of the tag */ +#define CVMX_TAG_SUBGROUP_MASK	0xFFFF +#define CVMX_TAG_SUBGROUP_SHIFT 16 +#define CVMX_TAG_SUBGROUP_PKO	0x1 + +/* End of executive tag subgroup definitions */ + +/* The remaining values software bit values 0x2 - 0xff are available + * for application use */ + +/** + * This function creates a 32 bit tag value from the two values provided. + * + * @param sw_bits The upper bits (number depends on configuration) are set + *     to this value.  The remainder of bits are set by the hw_bits parameter. + * @param hw_bits The lower bits (number depends on configuration) are set + *     to this value.  The remainder of bits are set by the sw_bits parameter. + * + * @return 32 bit value of the combined hw and sw bits. + */ +static inline u32 cvmx_pow_tag_compose(u64 sw_bits, u64 hw_bits) +{ +	return (((sw_bits & cvmx_build_mask(CVMX_TAG_SW_BITS)) << CVMX_TAG_SW_SHIFT) | +		(hw_bits & cvmx_build_mask(32 - CVMX_TAG_SW_BITS))); +} + +/** + * Extracts the bits allocated for software use from the tag + * + * @param tag    32 bit tag value + * + * @return N bit software tag value, where N is configurable with + *     the CVMX_TAG_SW_BITS define + */ +static inline u32 cvmx_pow_tag_get_sw_bits(u64 tag) +{ +	return ((tag >> (32 - CVMX_TAG_SW_BITS)) & cvmx_build_mask(CVMX_TAG_SW_BITS)); +} + +/** + * + * Extracts the bits allocated for hardware use from the tag + * + * @param tag    32 bit tag value + * + * @return (32 - N) bit software tag value, where N is configurable with + *     the CVMX_TAG_SW_BITS define + */ +static inline u32 cvmx_pow_tag_get_hw_bits(u64 tag) +{ +	return (tag & cvmx_build_mask(32 - CVMX_TAG_SW_BITS)); +} + +static inline u64 cvmx_sso3_get_wqe_count(int node) +{ +	cvmx_sso_grpx_aq_cnt_t aq_cnt; +	unsigned int grp = 0; +	u64 cnt = 0; + +	for (grp = 0; grp < cvmx_sso_num_xgrp(); grp++) { +		aq_cnt.u64 = csr_rd_node(node, CVMX_SSO_GRPX_AQ_CNT(grp)); +		cnt += aq_cnt.s.aq_cnt; +	} +	return cnt; +} + +static inline u64 cvmx_sso_get_total_wqe_count(void) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		int node = cvmx_get_node_num(); + +		return cvmx_sso3_get_wqe_count(node); +	} else if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { +		cvmx_sso_iq_com_cnt_t sso_iq_com_cnt; + +		sso_iq_com_cnt.u64 = csr_rd(CVMX_SSO_IQ_COM_CNT); +		return (sso_iq_com_cnt.s.iq_cnt); +	} else { +		cvmx_pow_iq_com_cnt_t pow_iq_com_cnt; + +		pow_iq_com_cnt.u64 = csr_rd(CVMX_POW_IQ_COM_CNT); +		return (pow_iq_com_cnt.s.iq_cnt); +	} +} + +/** + * Store the current POW internal state into the supplied + * buffer. It is recommended that you pass a buffer of at least + * 128KB. The format of the capture may change based on SDK + * version and Octeon chip. + * + * @param buffer Buffer to store capture into + * @param buffer_size The size of the supplied buffer + * + * @return Zero on success, negative on failure + */ +int cvmx_pow_capture(void *buffer, int buffer_size); + +/** + * Dump a POW capture to the console in a human readable format. + * + * @param buffer POW capture from cvmx_pow_capture() + * @param buffer_size Size of the buffer + */ +void cvmx_pow_display(void *buffer, int buffer_size); + +/** + * Return the number of POW entries supported by this chip + * + * @return Number of POW entries + */ +int cvmx_pow_get_num_entries(void); +int cvmx_pow_get_dump_size(void); + +/** + * This will allocate count number of SSO groups on the specified node to the + * calling application. These groups will be for exclusive use of the + * application until they are freed. + * @param node The numa node for the allocation. + * @param base_group Pointer to the initial group, -1 to allocate anywhere. + * @param count  The number of consecutive groups to allocate. + * @return 0 on success and -1 on failure. + */ +int cvmx_sso_reserve_group_range(int node, int *base_group, int count); +#define cvmx_sso_allocate_group_range cvmx_sso_reserve_group_range +int cvmx_sso_reserve_group(int node); +#define cvmx_sso_allocate_group cvmx_sso_reserve_group +int cvmx_sso_release_group_range(int node, int base_group, int count); +int cvmx_sso_release_group(int node, int group); + +/** + * Show integrated SSO configuration. + * + * @param node	   node number + */ +int cvmx_sso_config_dump(unsigned int node); + +/** + * Show integrated SSO statistics. + * + * @param node	   node number + */ +int cvmx_sso_stats_dump(unsigned int node); + +/** + * Clear integrated SSO statistics. + * + * @param node	   node number + */ +int cvmx_sso_stats_clear(unsigned int node); + +/** + * Show SSO core-group affinity and priority per node (multi-node systems) + */ +void cvmx_pow_mask_priority_dump_node(unsigned int node, struct cvmx_coremask *avail_coremask); + +/** + * Show POW/SSO core-group affinity and priority (legacy, single-node systems) + */ +static inline void cvmx_pow_mask_priority_dump(struct cvmx_coremask *avail_coremask) +{ +	cvmx_pow_mask_priority_dump_node(0 /*node */, avail_coremask); +} + +/** + * Show SSO performance counters (multi-node systems) + */ +void cvmx_pow_show_perf_counters_node(unsigned int node); + +/** + * Show POW/SSO performance counters (legacy, single-node systems) + */ +static inline void cvmx_pow_show_perf_counters(void) +{ +	cvmx_pow_show_perf_counters_node(0 /*node */); +} + +#endif /* __CVMX_POW_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-qlm.h b/arch/mips/mach-octeon/include/mach/cvmx-qlm.h new file mode 100644 index 00000000000..19915eb82c5 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-qlm.h @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + */ + +#ifndef __CVMX_QLM_H__ +#define __CVMX_QLM_H__ + +/* + * Interface 0 on the 78xx can be connected to qlm 0 or qlm 2. When interface + * 0 is connected to qlm 0, this macro must be set to 0. When interface 0 is + * connected to qlm 2, this macro must be set to 1. + */ +#define MUX_78XX_IFACE0 0 + +/* + * Interface 1 on the 78xx can be connected to qlm 1 or qlm 3. When interface + * 1 is connected to qlm 1, this macro must be set to 0. When interface 1 is + * connected to qlm 3, this macro must be set to 1. + */ +#define MUX_78XX_IFACE1 0 + +/* Uncomment this line to print QLM JTAG state */ +/* #define CVMX_QLM_DUMP_STATE 1 */ + +typedef struct { +	const char *name; +	int stop_bit; +	int start_bit; +} __cvmx_qlm_jtag_field_t; + +/** + * Return the number of QLMs supported by the chip + * + * @return  Number of QLMs + */ +int cvmx_qlm_get_num(void); + +/** + * Return the qlm number based on the interface + * + * @param xiface  Interface to look + */ +int cvmx_qlm_interface(int xiface); + +/** + * Return the qlm number based for a port in the interface + * + * @param xiface  interface to look up + * @param index  index in an interface + * + * @return the qlm number based on the xiface + */ +int cvmx_qlm_lmac(int xiface, int index); + +/** + * Return if only DLM5/DLM6/DLM5+DLM6 is used by BGX + * + * @param BGX  BGX to search for. + * + * @return muxes used 0 = DLM5+DLM6, 1 = DLM5, 2 = DLM6. + */ +int cvmx_qlm_mux_interface(int bgx); + +/** + * Return number of lanes for a given qlm + * + * @param qlm QLM block to query + * + * @return  Number of lanes + */ +int cvmx_qlm_get_lanes(int qlm); + +/** + * Get the QLM JTAG fields based on Octeon model on the supported chips. + * + * @return  qlm_jtag_field_t structure + */ +const __cvmx_qlm_jtag_field_t *cvmx_qlm_jtag_get_field(void); + +/** + * Get the QLM JTAG length by going through qlm_jtag_field for each + * Octeon model that is supported + * + * @return return the length. + */ +int cvmx_qlm_jtag_get_length(void); + +/** + * Initialize the QLM layer + */ +void cvmx_qlm_init(void); + +/** + * Get a field in a QLM JTAG chain + * + * @param qlm    QLM to get + * @param lane   Lane in QLM to get + * @param name   String name of field + * + * @return JTAG field value + */ +u64 cvmx_qlm_jtag_get(int qlm, int lane, const char *name); + +/** + * Set a field in a QLM JTAG chain + * + * @param qlm    QLM to set + * @param lane   Lane in QLM to set, or -1 for all lanes + * @param name   String name of field + * @param value  Value of the field + */ +void cvmx_qlm_jtag_set(int qlm, int lane, const char *name, u64 value); + +/** + * Errata G-16094: QLM Gen2 Equalizer Default Setting Change. + * CN68XX pass 1.x and CN66XX pass 1.x QLM tweak. This function tweaks the + * JTAG setting for a QLMs to run better at 5 and 6.25Ghz. + */ +void __cvmx_qlm_speed_tweak(void); + +/** + * Errata G-16174: QLM Gen2 PCIe IDLE DAC change. + * CN68XX pass 1.x, CN66XX pass 1.x and CN63XX pass 1.0-2.2 QLM tweak. + * This function tweaks the JTAG setting for a QLMs for PCIe to run better. + */ +void __cvmx_qlm_pcie_idle_dac_tweak(void); + +void __cvmx_qlm_pcie_cfg_rxd_set_tweak(int qlm, int lane); + +/** + * Get the speed (Gbaud) of the QLM in Mhz. + * + * @param qlm    QLM to examine + * + * @return Speed in Mhz + */ +int cvmx_qlm_get_gbaud_mhz(int qlm); +/** + * Get the speed (Gbaud) of the QLM in Mhz on specific node. + * + * @param node   Target QLM node + * @param qlm    QLM to examine + * + * @return Speed in Mhz + */ +int cvmx_qlm_get_gbaud_mhz_node(int node, int qlm); + +enum cvmx_qlm_mode { +	CVMX_QLM_MODE_DISABLED = -1, +	CVMX_QLM_MODE_SGMII = 1, +	CVMX_QLM_MODE_XAUI, +	CVMX_QLM_MODE_RXAUI, +	CVMX_QLM_MODE_PCIE,	/* gen3 / gen2 / gen1 */ +	CVMX_QLM_MODE_PCIE_1X2, /* 1x2 gen2 / gen1 */ +	CVMX_QLM_MODE_PCIE_2X1, /* 2x1 gen2 / gen1 */ +	CVMX_QLM_MODE_PCIE_1X1, /* 1x1 gen2 / gen1 */ +	CVMX_QLM_MODE_SRIO_1X4, /* 1x4 short / long */ +	CVMX_QLM_MODE_SRIO_2X2, /* 2x2 short / long */ +	CVMX_QLM_MODE_SRIO_4X1, /* 4x1 short / long */ +	CVMX_QLM_MODE_ILK, +	CVMX_QLM_MODE_QSGMII, +	CVMX_QLM_MODE_SGMII_SGMII, +	CVMX_QLM_MODE_SGMII_DISABLED, +	CVMX_QLM_MODE_DISABLED_SGMII, +	CVMX_QLM_MODE_SGMII_QSGMII, +	CVMX_QLM_MODE_QSGMII_QSGMII, +	CVMX_QLM_MODE_QSGMII_DISABLED, +	CVMX_QLM_MODE_DISABLED_QSGMII, +	CVMX_QLM_MODE_QSGMII_SGMII, +	CVMX_QLM_MODE_RXAUI_1X2, +	CVMX_QLM_MODE_SATA_2X1, +	CVMX_QLM_MODE_XLAUI, +	CVMX_QLM_MODE_XFI, +	CVMX_QLM_MODE_10G_KR, +	CVMX_QLM_MODE_40G_KR4, +	CVMX_QLM_MODE_PCIE_1X8, /* 1x8 gen3 / gen2 / gen1 */ +	CVMX_QLM_MODE_RGMII_SGMII, +	CVMX_QLM_MODE_RGMII_XFI, +	CVMX_QLM_MODE_RGMII_10G_KR, +	CVMX_QLM_MODE_RGMII_RXAUI, +	CVMX_QLM_MODE_RGMII_XAUI, +	CVMX_QLM_MODE_RGMII_XLAUI, +	CVMX_QLM_MODE_RGMII_40G_KR4, +	CVMX_QLM_MODE_MIXED,		/* BGX2 is mixed mode, DLM5(SGMII) & DLM6(XFI) */ +	CVMX_QLM_MODE_SGMII_2X1,	/* Configure BGX2 separate for DLM5 & DLM6 */ +	CVMX_QLM_MODE_10G_KR_1X2,	/* Configure BGX2 separate for DLM5 & DLM6 */ +	CVMX_QLM_MODE_XFI_1X2,		/* Configure BGX2 separate for DLM5 & DLM6 */ +	CVMX_QLM_MODE_RGMII_SGMII_1X1,	/* Configure BGX2, applies to DLM5 */ +	CVMX_QLM_MODE_RGMII_SGMII_2X1,	/* Configure BGX2, applies to DLM6 */ +	CVMX_QLM_MODE_RGMII_10G_KR_1X1, /* Configure BGX2, applies to DLM6 */ +	CVMX_QLM_MODE_RGMII_XFI_1X1,	/* Configure BGX2, applies to DLM6 */ +	CVMX_QLM_MODE_SDL,		/* RMAC Pipe */ +	CVMX_QLM_MODE_CPRI,		/* RMAC */ +	CVMX_QLM_MODE_OCI +}; + +enum cvmx_gmx_inf_mode { +	CVMX_GMX_INF_MODE_DISABLED = 0, +	CVMX_GMX_INF_MODE_SGMII = 1,  /* Other interface can be SGMII or QSGMII */ +	CVMX_GMX_INF_MODE_QSGMII = 2, /* Other interface can be SGMII or QSGMII */ +	CVMX_GMX_INF_MODE_RXAUI = 3,  /* Only interface 0, interface 1 must be DISABLED */ +}; + +/** + * Eye diagram captures are stored in the following structure + */ +typedef struct { +	int width;	   /* Width in the x direction (time) */ +	int height;	   /* Height in the y direction (voltage) */ +	u32 data[64][128]; /* Error count at location, saturates as max */ +} cvmx_qlm_eye_t; + +/** + * These apply to DLM1 and DLM2 if its not in SATA mode + * Manual refers to lanes as follows: + *  DML 0 lane 0 == GSER0 lane 0 + *  DML 0 lane 1 == GSER0 lane 1 + *  DML 1 lane 2 == GSER1 lane 0 + *  DML 1 lane 3 == GSER1 lane 1 + *  DML 2 lane 4 == GSER2 lane 0 + *  DML 2 lane 5 == GSER2 lane 1 + */ +enum cvmx_pemx_cfg_mode { +	CVMX_PEM_MD_GEN2_2LANE = 0, /* Valid for PEM0(DLM1), PEM1(DLM2) */ +	CVMX_PEM_MD_GEN2_1LANE = 1, /* Valid for PEM0(DLM1.0), PEM1(DLM1.1,DLM2.0), PEM2(DLM2.1) */ +	CVMX_PEM_MD_GEN2_4LANE = 2, /* Valid for PEM0(DLM1-2) */ +	/* Reserved */ +	CVMX_PEM_MD_GEN1_2LANE = 4, /* Valid for PEM0(DLM1), PEM1(DLM2) */ +	CVMX_PEM_MD_GEN1_1LANE = 5, /* Valid for PEM0(DLM1.0), PEM1(DLM1.1,DLM2.0), PEM2(DLM2.1) */ +	CVMX_PEM_MD_GEN1_4LANE = 6, /* Valid for PEM0(DLM1-2) */ +	/* Reserved */ +}; + +/* + * Read QLM and return mode. + */ +enum cvmx_qlm_mode cvmx_qlm_get_mode(int qlm); +enum cvmx_qlm_mode cvmx_qlm_get_mode_cn78xx(int node, int qlm); +enum cvmx_qlm_mode cvmx_qlm_get_dlm_mode(int dlm_mode, int interface); +void __cvmx_qlm_set_mult(int qlm, int baud_mhz, int old_multiplier); + +void cvmx_qlm_display_registers(int qlm); + +int cvmx_qlm_measure_clock(int qlm); + +/** + * Measure the reference clock of a QLM on a multi-node setup + * + * @param node   node to measure + * @param qlm    QLM to measure + * + * @return Clock rate in Hz + */ +int cvmx_qlm_measure_clock_node(int node, int qlm); + +/* + * Perform RX equalization on a QLM + * + * @param node	Node the QLM is on + * @param qlm	QLM to perform RX equalization on + * @param lane	Lane to use, or -1 for all lanes + * + * @return Zero on success, negative if any lane failed RX equalization + */ +int __cvmx_qlm_rx_equalization(int node, int qlm, int lane); + +/** + * Errata GSER-27882 -GSER 10GBASE-KR Transmit Equalizer + * Training may not update PHY Tx Taps. This function is not static + * so we can share it with BGX KR + * + * @param node	Node to apply errata workaround + * @param qlm	QLM to apply errata workaround + * @param lane	Lane to apply the errata + */ +int cvmx_qlm_gser_errata_27882(int node, int qlm, int lane); + +void cvmx_qlm_gser_errata_25992(int node, int qlm); + +#ifdef CVMX_DUMP_GSER +/** + * Dump GSER configuration for node 0 + */ +int cvmx_dump_gser_config(unsigned int gser); +/** + * Dump GSER status for node 0 + */ +int cvmx_dump_gser_status(unsigned int gser); +/** + * Dump GSER configuration + */ +int cvmx_dump_gser_config_node(unsigned int node, unsigned int gser); +/** + * Dump GSER status + */ +int cvmx_dump_gser_status_node(unsigned int node, unsigned int gser); +#endif + +int cvmx_qlm_eye_display(int node, int qlm, int qlm_lane, int format, const cvmx_qlm_eye_t *eye); + +void cvmx_prbs_process_cmd(int node, int qlm, int mode); + +#endif /* __CVMX_QLM_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-scratch.h b/arch/mips/mach-octeon/include/mach/cvmx-scratch.h new file mode 100644 index 00000000000..d567a8453b7 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-scratch.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * This file provides support for the processor local scratch memory. + * Scratch memory is byte addressable - all addresses are byte addresses. + */ + +#ifndef __CVMX_SCRATCH_H__ +#define __CVMX_SCRATCH_H__ + +/* Note: This define must be a long, not a long long in order to compile +	without warnings for both 32bit and 64bit. */ +#define CVMX_SCRATCH_BASE (-32768l) /* 0xffffffffffff8000 */ + +/* Scratch line for LMTST/LMTDMA on Octeon3 models */ +#ifdef CVMX_CAVIUM_OCTEON3 +#define CVMX_PKO_LMTLINE 2ull +#endif + +/** + * Reads an 8 bit value from the processor local scratchpad memory. + * + * @param address byte address to read from + * + * @return value read + */ +static inline u8 cvmx_scratch_read8(u64 address) +{ +	return *CASTPTR(volatile u8, CVMX_SCRATCH_BASE + address); +} + +/** + * Reads a 16 bit value from the processor local scratchpad memory. + * + * @param address byte address to read from + * + * @return value read + */ +static inline u16 cvmx_scratch_read16(u64 address) +{ +	return *CASTPTR(volatile u16, CVMX_SCRATCH_BASE + address); +} + +/** + * Reads a 32 bit value from the processor local scratchpad memory. + * + * @param address byte address to read from + * + * @return value read + */ +static inline u32 cvmx_scratch_read32(u64 address) +{ +	return *CASTPTR(volatile u32, CVMX_SCRATCH_BASE + address); +} + +/** + * Reads a 64 bit value from the processor local scratchpad memory. + * + * @param address byte address to read from + * + * @return value read + */ +static inline u64 cvmx_scratch_read64(u64 address) +{ +	return *CASTPTR(volatile u64, CVMX_SCRATCH_BASE + address); +} + +/** + * Writes an 8 bit value to the processor local scratchpad memory. + * + * @param address byte address to write to + * @param value   value to write + */ +static inline void cvmx_scratch_write8(u64 address, u64 value) +{ +	*CASTPTR(volatile u8, CVMX_SCRATCH_BASE + address) = (u8)value; +} + +/** + * Writes a 32 bit value to the processor local scratchpad memory. + * + * @param address byte address to write to + * @param value   value to write + */ +static inline void cvmx_scratch_write16(u64 address, u64 value) +{ +	*CASTPTR(volatile u16, CVMX_SCRATCH_BASE + address) = (u16)value; +} + +/** + * Writes a 16 bit value to the processor local scratchpad memory. + * + * @param address byte address to write to + * @param value   value to write + */ +static inline void cvmx_scratch_write32(u64 address, u64 value) +{ +	*CASTPTR(volatile u32, CVMX_SCRATCH_BASE + address) = (u32)value; +} + +/** + * Writes a 64 bit value to the processor local scratchpad memory. + * + * @param address byte address to write to + * @param value   value to write + */ +static inline void cvmx_scratch_write64(u64 address, u64 value) +{ +	*CASTPTR(volatile u64, CVMX_SCRATCH_BASE + address) = value; +} + +#endif /* __CVMX_SCRATCH_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/cvmx-wqe.h b/arch/mips/mach-octeon/include/mach/cvmx-wqe.h new file mode 100644 index 00000000000..c9e3c8312a6 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/cvmx-wqe.h @@ -0,0 +1,1462 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + * + * This header file defines the work queue entry (wqe) data structure. + * Since this is a commonly used structure that depends on structures + * from several hardware blocks, those definitions have been placed + * in this file to create a single point of definition of the wqe + * format. + * Data structures are still named according to the block that they + * relate to. + */ + +#ifndef __CVMX_WQE_H__ +#define __CVMX_WQE_H__ + +#include "cvmx-packet.h" +#include "cvmx-csr-enums.h" +#include "cvmx-pki-defs.h" +#include "cvmx-pip-defs.h" +#include "octeon-feature.h" + +#define OCT_TAG_TYPE_STRING(x)						\ +	(((x) == CVMX_POW_TAG_TYPE_ORDERED) ?				\ +	 "ORDERED" :							\ +	 (((x) == CVMX_POW_TAG_TYPE_ATOMIC) ?				\ +	  "ATOMIC" :							\ +	  (((x) == CVMX_POW_TAG_TYPE_NULL) ? "NULL" : "NULL_NULL"))) + +/* Error levels in WQE WORD2 (ERRLEV).*/ +#define PKI_ERRLEV_E__RE_M 0x0 +#define PKI_ERRLEV_E__LA_M 0x1 +#define PKI_ERRLEV_E__LB_M 0x2 +#define PKI_ERRLEV_E__LC_M 0x3 +#define PKI_ERRLEV_E__LD_M 0x4 +#define PKI_ERRLEV_E__LE_M 0x5 +#define PKI_ERRLEV_E__LF_M 0x6 +#define PKI_ERRLEV_E__LG_M 0x7 + +enum cvmx_pki_errlevel { +	CVMX_PKI_ERRLEV_E_RE = PKI_ERRLEV_E__RE_M, +	CVMX_PKI_ERRLEV_E_LA = PKI_ERRLEV_E__LA_M, +	CVMX_PKI_ERRLEV_E_LB = PKI_ERRLEV_E__LB_M, +	CVMX_PKI_ERRLEV_E_LC = PKI_ERRLEV_E__LC_M, +	CVMX_PKI_ERRLEV_E_LD = PKI_ERRLEV_E__LD_M, +	CVMX_PKI_ERRLEV_E_LE = PKI_ERRLEV_E__LE_M, +	CVMX_PKI_ERRLEV_E_LF = PKI_ERRLEV_E__LF_M, +	CVMX_PKI_ERRLEV_E_LG = PKI_ERRLEV_E__LG_M +}; + +#define CVMX_PKI_ERRLEV_MAX BIT(3) /* The size of WORD2:ERRLEV field.*/ + +/* Error code in WQE WORD2 (OPCODE).*/ +#define CVMX_PKI_OPCODE_RE_NONE	      0x0 +#define CVMX_PKI_OPCODE_RE_PARTIAL    0x1 +#define CVMX_PKI_OPCODE_RE_JABBER     0x2 +#define CVMX_PKI_OPCODE_RE_FCS	      0x7 +#define CVMX_PKI_OPCODE_RE_FCS_RCV    0x8 +#define CVMX_PKI_OPCODE_RE_TERMINATE  0x9 +#define CVMX_PKI_OPCODE_RE_RX_CTL     0xb +#define CVMX_PKI_OPCODE_RE_SKIP	      0xc +#define CVMX_PKI_OPCODE_RE_DMAPKT     0xf +#define CVMX_PKI_OPCODE_RE_PKIPAR     0x13 +#define CVMX_PKI_OPCODE_RE_PKIPCAM    0x14 +#define CVMX_PKI_OPCODE_RE_MEMOUT     0x15 +#define CVMX_PKI_OPCODE_RE_BUFS_OFLOW 0x16 +#define CVMX_PKI_OPCODE_L2_FRAGMENT   0x20 +#define CVMX_PKI_OPCODE_L2_OVERRUN    0x21 +#define CVMX_PKI_OPCODE_L2_PFCS	      0x22 +#define CVMX_PKI_OPCODE_L2_PUNY	      0x23 +#define CVMX_PKI_OPCODE_L2_MAL	      0x24 +#define CVMX_PKI_OPCODE_L2_OVERSIZE   0x25 +#define CVMX_PKI_OPCODE_L2_UNDERSIZE  0x26 +#define CVMX_PKI_OPCODE_L2_LENMISM    0x27 +#define CVMX_PKI_OPCODE_IP_NOT	      0x41 +#define CVMX_PKI_OPCODE_IP_CHK	      0x42 +#define CVMX_PKI_OPCODE_IP_MAL	      0x43 +#define CVMX_PKI_OPCODE_IP_MALD	      0x44 +#define CVMX_PKI_OPCODE_IP_HOP	      0x45 +#define CVMX_PKI_OPCODE_L4_MAL	      0x61 +#define CVMX_PKI_OPCODE_L4_CHK	      0x62 +#define CVMX_PKI_OPCODE_L4_LEN	      0x63 +#define CVMX_PKI_OPCODE_L4_PORT	      0x64 +#define CVMX_PKI_OPCODE_TCP_FLAG      0x65 + +#define CVMX_PKI_OPCODE_MAX BIT(8) /* The size of WORD2:OPCODE field.*/ + +/* Layer types in pki */ +#define CVMX_PKI_LTYPE_E_NONE_M	      0x0 +#define CVMX_PKI_LTYPE_E_ENET_M	      0x1 +#define CVMX_PKI_LTYPE_E_VLAN_M	      0x2 +#define CVMX_PKI_LTYPE_E_SNAP_PAYLD_M 0x5 +#define CVMX_PKI_LTYPE_E_ARP_M	      0x6 +#define CVMX_PKI_LTYPE_E_RARP_M	      0x7 +#define CVMX_PKI_LTYPE_E_IP4_M	      0x8 +#define CVMX_PKI_LTYPE_E_IP4_OPT_M    0x9 +#define CVMX_PKI_LTYPE_E_IP6_M	      0xA +#define CVMX_PKI_LTYPE_E_IP6_OPT_M    0xB +#define CVMX_PKI_LTYPE_E_IPSEC_ESP_M  0xC +#define CVMX_PKI_LTYPE_E_IPFRAG_M     0xD +#define CVMX_PKI_LTYPE_E_IPCOMP_M     0xE +#define CVMX_PKI_LTYPE_E_TCP_M	      0x10 +#define CVMX_PKI_LTYPE_E_UDP_M	      0x11 +#define CVMX_PKI_LTYPE_E_SCTP_M	      0x12 +#define CVMX_PKI_LTYPE_E_UDP_VXLAN_M  0x13 +#define CVMX_PKI_LTYPE_E_GRE_M	      0x14 +#define CVMX_PKI_LTYPE_E_NVGRE_M      0x15 +#define CVMX_PKI_LTYPE_E_GTP_M	      0x16 +#define CVMX_PKI_LTYPE_E_SW28_M	      0x1C +#define CVMX_PKI_LTYPE_E_SW29_M	      0x1D +#define CVMX_PKI_LTYPE_E_SW30_M	      0x1E +#define CVMX_PKI_LTYPE_E_SW31_M	      0x1F + +enum cvmx_pki_layer_type { +	CVMX_PKI_LTYPE_E_NONE = CVMX_PKI_LTYPE_E_NONE_M, +	CVMX_PKI_LTYPE_E_ENET = CVMX_PKI_LTYPE_E_ENET_M, +	CVMX_PKI_LTYPE_E_VLAN = CVMX_PKI_LTYPE_E_VLAN_M, +	CVMX_PKI_LTYPE_E_SNAP_PAYLD = CVMX_PKI_LTYPE_E_SNAP_PAYLD_M, +	CVMX_PKI_LTYPE_E_ARP = CVMX_PKI_LTYPE_E_ARP_M, +	CVMX_PKI_LTYPE_E_RARP = CVMX_PKI_LTYPE_E_RARP_M, +	CVMX_PKI_LTYPE_E_IP4 = CVMX_PKI_LTYPE_E_IP4_M, +	CVMX_PKI_LTYPE_E_IP4_OPT = CVMX_PKI_LTYPE_E_IP4_OPT_M, +	CVMX_PKI_LTYPE_E_IP6 = CVMX_PKI_LTYPE_E_IP6_M, +	CVMX_PKI_LTYPE_E_IP6_OPT = CVMX_PKI_LTYPE_E_IP6_OPT_M, +	CVMX_PKI_LTYPE_E_IPSEC_ESP = CVMX_PKI_LTYPE_E_IPSEC_ESP_M, +	CVMX_PKI_LTYPE_E_IPFRAG = CVMX_PKI_LTYPE_E_IPFRAG_M, +	CVMX_PKI_LTYPE_E_IPCOMP = CVMX_PKI_LTYPE_E_IPCOMP_M, +	CVMX_PKI_LTYPE_E_TCP = CVMX_PKI_LTYPE_E_TCP_M, +	CVMX_PKI_LTYPE_E_UDP = CVMX_PKI_LTYPE_E_UDP_M, +	CVMX_PKI_LTYPE_E_SCTP = CVMX_PKI_LTYPE_E_SCTP_M, +	CVMX_PKI_LTYPE_E_UDP_VXLAN = CVMX_PKI_LTYPE_E_UDP_VXLAN_M, +	CVMX_PKI_LTYPE_E_GRE = CVMX_PKI_LTYPE_E_GRE_M, +	CVMX_PKI_LTYPE_E_NVGRE = CVMX_PKI_LTYPE_E_NVGRE_M, +	CVMX_PKI_LTYPE_E_GTP = CVMX_PKI_LTYPE_E_GTP_M, +	CVMX_PKI_LTYPE_E_SW28 = CVMX_PKI_LTYPE_E_SW28_M, +	CVMX_PKI_LTYPE_E_SW29 = CVMX_PKI_LTYPE_E_SW29_M, +	CVMX_PKI_LTYPE_E_SW30 = CVMX_PKI_LTYPE_E_SW30_M, +	CVMX_PKI_LTYPE_E_SW31 = CVMX_PKI_LTYPE_E_SW31_M, +	CVMX_PKI_LTYPE_E_MAX = CVMX_PKI_LTYPE_E_SW31 +}; + +typedef union { +	u64 u64; +	struct { +		u64 ptr_vlan : 8; +		u64 ptr_layer_g : 8; +		u64 ptr_layer_f : 8; +		u64 ptr_layer_e : 8; +		u64 ptr_layer_d : 8; +		u64 ptr_layer_c : 8; +		u64 ptr_layer_b : 8; +		u64 ptr_layer_a : 8; +	}; +} cvmx_pki_wqe_word4_t; + +/** + * HW decode / err_code in work queue entry + */ +typedef union { +	u64 u64; +	struct { +		u64 bufs : 8; +		u64 ip_offset : 8; +		u64 vlan_valid : 1; +		u64 vlan_stacked : 1; +		u64 unassigned : 1; +		u64 vlan_cfi : 1; +		u64 vlan_id : 12; +		u64 varies : 12; +		u64 dec_ipcomp : 1; +		u64 tcp_or_udp : 1; +		u64 dec_ipsec : 1; +		u64 is_v6 : 1; +		u64 software : 1; +		u64 L4_error : 1; +		u64 is_frag : 1; +		u64 IP_exc : 1; +		u64 is_bcast : 1; +		u64 is_mcast : 1; +		u64 not_IP : 1; +		u64 rcv_error : 1; +		u64 err_code : 8; +	} s; +	struct { +		u64 bufs : 8; +		u64 ip_offset : 8; +		u64 vlan_valid : 1; +		u64 vlan_stacked : 1; +		u64 unassigned : 1; +		u64 vlan_cfi : 1; +		u64 vlan_id : 12; +		u64 port : 12; +		u64 dec_ipcomp : 1; +		u64 tcp_or_udp : 1; +		u64 dec_ipsec : 1; +		u64 is_v6 : 1; +		u64 software : 1; +		u64 L4_error : 1; +		u64 is_frag : 1; +		u64 IP_exc : 1; +		u64 is_bcast : 1; +		u64 is_mcast : 1; +		u64 not_IP : 1; +		u64 rcv_error : 1; +		u64 err_code : 8; +	} s_cn68xx; +	struct { +		u64 bufs : 8; +		u64 ip_offset : 8; +		u64 vlan_valid : 1; +		u64 vlan_stacked : 1; +		u64 unassigned : 1; +		u64 vlan_cfi : 1; +		u64 vlan_id : 12; +		u64 pr : 4; +		u64 unassigned2a : 4; +		u64 unassigned2 : 4; +		u64 dec_ipcomp : 1; +		u64 tcp_or_udp : 1; +		u64 dec_ipsec : 1; +		u64 is_v6 : 1; +		u64 software : 1; +		u64 L4_error : 1; +		u64 is_frag : 1; +		u64 IP_exc : 1; +		u64 is_bcast : 1; +		u64 is_mcast : 1; +		u64 not_IP : 1; +		u64 rcv_error : 1; +		u64 err_code : 8; +	} s_cn38xx; +	struct { +		u64 unused1 : 16; +		u64 vlan : 16; +		u64 unused2 : 32; +	} svlan; +	struct { +		u64 bufs : 8; +		u64 unused : 8; +		u64 vlan_valid : 1; +		u64 vlan_stacked : 1; +		u64 unassigned : 1; +		u64 vlan_cfi : 1; +		u64 vlan_id : 12; +		u64 varies : 12; +		u64 unassigned2 : 4; +		u64 software : 1; +		u64 unassigned3 : 1; +		u64 is_rarp : 1; +		u64 is_arp : 1; +		u64 is_bcast : 1; +		u64 is_mcast : 1; +		u64 not_IP : 1; +		u64 rcv_error : 1; +		u64 err_code : 8; +	} snoip; +	struct { +		u64 bufs : 8; +		u64 unused : 8; +		u64 vlan_valid : 1; +		u64 vlan_stacked : 1; +		u64 unassigned : 1; +		u64 vlan_cfi : 1; +		u64 vlan_id : 12; +		u64 port : 12; +		u64 unassigned2 : 4; +		u64 software : 1; +		u64 unassigned3 : 1; +		u64 is_rarp : 1; +		u64 is_arp : 1; +		u64 is_bcast : 1; +		u64 is_mcast : 1; +		u64 not_IP : 1; +		u64 rcv_error : 1; +		u64 err_code : 8; +	} snoip_cn68xx; +	struct { +		u64 bufs : 8; +		u64 unused : 8; +		u64 vlan_valid : 1; +		u64 vlan_stacked : 1; +		u64 unassigned : 1; +		u64 vlan_cfi : 1; +		u64 vlan_id : 12; +		u64 pr : 4; +		u64 unassigned2a : 8; +		u64 unassigned2 : 4; +		u64 software : 1; +		u64 unassigned3 : 1; +		u64 is_rarp : 1; +		u64 is_arp : 1; +		u64 is_bcast : 1; +		u64 is_mcast : 1; +		u64 not_IP : 1; +		u64 rcv_error : 1; +		u64 err_code : 8; +	} snoip_cn38xx; +} cvmx_pip_wqe_word2_t; + +typedef union { +	u64 u64; +	struct { +		u64 software : 1; +		u64 lg_hdr_type : 5; +		u64 lf_hdr_type : 5; +		u64 le_hdr_type : 5; +		u64 ld_hdr_type : 5; +		u64 lc_hdr_type : 5; +		u64 lb_hdr_type : 5; +		u64 is_la_ether : 1; +		u64 rsvd_0 : 8; +		u64 vlan_valid : 1; +		u64 vlan_stacked : 1; +		u64 stat_inc : 1; +		u64 pcam_flag4 : 1; +		u64 pcam_flag3 : 1; +		u64 pcam_flag2 : 1; +		u64 pcam_flag1 : 1; +		u64 is_frag : 1; +		u64 is_l3_bcast : 1; +		u64 is_l3_mcast : 1; +		u64 is_l2_bcast : 1; +		u64 is_l2_mcast : 1; +		u64 is_raw : 1; +		u64 err_level : 3; +		u64 err_code : 8; +	}; +} cvmx_pki_wqe_word2_t; + +typedef union { +	u64 u64; +	cvmx_pki_wqe_word2_t pki; +	cvmx_pip_wqe_word2_t pip; +} cvmx_wqe_word2_t; + +typedef union { +	u64 u64; +	struct { +		u16 hw_chksum; +		u8 unused; +		u64 next_ptr : 40; +	} cn38xx; +	struct { +		u64 l4ptr : 8;	  /* 56..63 */ +		u64 unused0 : 8;  /* 48..55 */ +		u64 l3ptr : 8;	  /* 40..47 */ +		u64 l2ptr : 8;	  /* 32..39 */ +		u64 unused1 : 18; /* 14..31 */ +		u64 bpid : 6;	  /* 8..13 */ +		u64 unused2 : 2;  /* 6..7 */ +		u64 pknd : 6;	  /* 0..5 */ +	} cn68xx; +} cvmx_pip_wqe_word0_t; + +typedef union { +	u64 u64; +	struct { +		u64 rsvd_0 : 4; +		u64 aura : 12; +		u64 rsvd_1 : 1; +		u64 apad : 3; +		u64 channel : 12; +		u64 bufs : 8; +		u64 style : 8; +		u64 rsvd_2 : 10; +		u64 pknd : 6; +	}; +} cvmx_pki_wqe_word0_t; + +/* Use reserved bit, set by HW to 0, to indicate buf_ptr legacy translation*/ +#define pki_wqe_translated word0.rsvd_1 + +typedef union { +	u64 u64; +	cvmx_pip_wqe_word0_t pip; +	cvmx_pki_wqe_word0_t pki; +	struct { +		u64 unused : 24; +		u64 next_ptr : 40; /* On cn68xx this is unused as well */ +	} raw; +} cvmx_wqe_word0_t; + +typedef union { +	u64 u64; +	struct { +		u64 len : 16; +		u64 rsvd_0 : 2; +		u64 rsvd_1 : 2; +		u64 grp : 10; +		cvmx_pow_tag_type_t tag_type : 2; +		u64 tag : 32; +	}; +} cvmx_pki_wqe_word1_t; + +#define pki_errata20776 word1.rsvd_0 + +typedef union { +	u64 u64; +	struct { +		u64 len : 16; +		u64 varies : 14; +		cvmx_pow_tag_type_t tag_type : 2; +		u64 tag : 32; +	}; +	cvmx_pki_wqe_word1_t cn78xx; +	struct { +		u64 len : 16; +		u64 zero_0 : 1; +		u64 qos : 3; +		u64 zero_1 : 1; +		u64 grp : 6; +		u64 zero_2 : 3; +		cvmx_pow_tag_type_t tag_type : 2; +		u64 tag : 32; +	} cn68xx; +	struct { +		u64 len : 16; +		u64 ipprt : 6; +		u64 qos : 3; +		u64 grp : 4; +		u64 zero_2 : 1; +		cvmx_pow_tag_type_t tag_type : 2; +		u64 tag : 32; +	} cn38xx; +} cvmx_wqe_word1_t; + +typedef union { +	u64 u64; +	struct { +		u64 rsvd_0 : 8; +		u64 hwerr : 8; +		u64 rsvd_1 : 24; +		u64 sqid : 8; +		u64 rsvd_2 : 4; +		u64 vfnum : 12; +	}; +} cvmx_wqe_word3_t; + +typedef union { +	u64 u64; +	struct { +		u64 rsvd_0 : 21; +		u64 sqfc : 11; +		u64 rsvd_1 : 5; +		u64 sqtail : 11; +		u64 rsvd_2 : 3; +		u64 sqhead : 13; +	}; +} cvmx_wqe_word4_t; + +/** + * Work queue entry format. + * Must be 8-byte aligned. + */ +typedef struct cvmx_wqe_s { +	/*-------------------------------------------------------------------*/ +	/* WORD 0                                                            */ +	/*-------------------------------------------------------------------*/ +	/* HW WRITE: the following 64 bits are filled by HW when a packet +	 * arrives. +	 */ +	cvmx_wqe_word0_t word0; + +	/*-------------------------------------------------------------------*/ +	/* WORD 1                                                            */ +	/*-------------------------------------------------------------------*/ +	/* HW WRITE: the following 64 bits are filled by HW when a packet +	 * arrives. +	 */ +	cvmx_wqe_word1_t word1; + +	/*-------------------------------------------------------------------*/ +	/* WORD 2                                                            */ +	/*-------------------------------------------------------------------*/ +	/* HW WRITE: the following 64-bits are filled in by hardware when a +	 * packet arrives. This indicates a variety of status and error +	 *conditions. +	 */ +	cvmx_pip_wqe_word2_t word2; + +	/* Pointer to the first segment of the packet. */ +	cvmx_buf_ptr_t packet_ptr; + +	/* HW WRITE: OCTEON will fill in a programmable amount from the packet, +	 * up to (at most, but perhaps less) the amount needed to fill the work +	 * queue entry to 128 bytes. If the packet is recognized to be IP, the +	 * hardware starts (except that the IPv4 header is padded for +	 * appropriate alignment) writing here where the IP header starts. +	 * If the packet is not recognized to be IP, the hardware starts +	 * writing the beginning of the packet here. +	 */ +	u8 packet_data[96]; + +	/* If desired, SW can make the work Q entry any length. For the purposes +	 * of discussion here, Assume 128B always, as this is all that the hardware +	 * deals with. +	 */ +} CVMX_CACHE_LINE_ALIGNED cvmx_wqe_t; + +/** + * Work queue entry format for NQM + * Must be 8-byte aligned + */ +typedef struct cvmx_wqe_nqm_s { +	/*-------------------------------------------------------------------*/ +	/* WORD 0                                                            */ +	/*-------------------------------------------------------------------*/ +	/* HW WRITE: the following 64 bits are filled by HW when a packet +	 * arrives. +	 */ +	cvmx_wqe_word0_t word0; + +	/*-------------------------------------------------------------------*/ +	/* WORD 1                                                            */ +	/*-------------------------------------------------------------------*/ +	/* HW WRITE: the following 64 bits are filled by HW when a packet +	 * arrives. +	 */ +	cvmx_wqe_word1_t word1; + +	/*-------------------------------------------------------------------*/ +	/* WORD 2                                                            */ +	/*-------------------------------------------------------------------*/ +	/* Reserved */ +	u64 word2; + +	/*-------------------------------------------------------------------*/ +	/* WORD 3                                                            */ +	/*-------------------------------------------------------------------*/ +	/* NVMe specific information.*/ +	cvmx_wqe_word3_t word3; + +	/*-------------------------------------------------------------------*/ +	/* WORD 4                                                            */ +	/*-------------------------------------------------------------------*/ +	/* NVMe specific information.*/ +	cvmx_wqe_word4_t word4; + +	/* HW WRITE: OCTEON will fill in a programmable amount from the packet, +	 * up to (at most, but perhaps less) the amount needed to fill the work +	 * queue entry to 128 bytes. If the packet is recognized to be IP, the +	 * hardware starts (except that the IPv4 header is padded for +	 * appropriate alignment) writing here where the IP header starts. +	 * If the packet is not recognized to be IP, the hardware starts +	 * writing the beginning of the packet here. +	 */ +	u8 packet_data[88]; + +	/* If desired, SW can make the work Q entry any length. +	 * For the purposes of discussion here, assume 128B always, as this is +	 * all that the hardware deals with. +	 */ +} CVMX_CACHE_LINE_ALIGNED cvmx_wqe_nqm_t; + +/** + * Work queue entry format for 78XX. + * In 78XX packet data always resides in WQE buffer unless option + * DIS_WQ_DAT=1 in PKI_STYLE_BUF, which causes packet data to use separate buffer. + * + * Must be 8-byte aligned. + */ +typedef struct { +	/*-------------------------------------------------------------------*/ +	/* WORD 0                                                            */ +	/*-------------------------------------------------------------------*/ +	/* HW WRITE: the following 64 bits are filled by HW when a packet +	 * arrives. +	 */ +	cvmx_pki_wqe_word0_t word0; + +	/*-------------------------------------------------------------------*/ +	/* WORD 1                                                            */ +	/*-------------------------------------------------------------------*/ +	/* HW WRITE: the following 64 bits are filled by HW when a packet +	 * arrives. +	 */ +	cvmx_pki_wqe_word1_t word1; + +	/*-------------------------------------------------------------------*/ +	/* WORD 2                                                            */ +	/*-------------------------------------------------------------------*/ +	/* HW WRITE: the following 64-bits are filled in by hardware when a +	 * packet arrives. This indicates a variety of status and error +	 * conditions. +	 */ +	cvmx_pki_wqe_word2_t word2; + +	/*-------------------------------------------------------------------*/ +	/* WORD 3                                                            */ +	/*-------------------------------------------------------------------*/ +	/* Pointer to the first segment of the packet.*/ +	cvmx_buf_ptr_pki_t packet_ptr; + +	/*-------------------------------------------------------------------*/ +	/* WORD 4                                                            */ +	/*-------------------------------------------------------------------*/ +	/* HW WRITE: the following 64-bits are filled in by hardware when a +	 * packet arrives contains a byte pointer to the start of Layer +	 * A/B/C/D/E/F/G relative of start of packet. +	 */ +	cvmx_pki_wqe_word4_t word4; + +	/*-------------------------------------------------------------------*/ +	/* WORDs 5/6/7 may be extended there, if WQE_HSZ is set.             */ +	/*-------------------------------------------------------------------*/ +	u64 wqe_data[11]; + +} CVMX_CACHE_LINE_ALIGNED cvmx_wqe_78xx_t; + +/* Node LS-bit position in the WQE[grp] or PKI_QPG_TBL[grp_ok].*/ +#define CVMX_WQE_GRP_NODE_SHIFT 8 + +/* + * This is an accessor function into the WQE that retreives the + * ingress port number, which can also be used as a destination + * port number for the same port. + * + * @param work - Work Queue Entrey pointer + * @returns returns the normalized port number, also known as "ipd" port + */ +static inline int cvmx_wqe_get_port(cvmx_wqe_t *work) +{ +	int port; + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		/* In 78xx wqe entry has channel number not port*/ +		port = work->word0.pki.channel; +		/* For BGX interfaces (0x800 - 0xdff) the 4 LSBs indicate +		 * the PFC channel, must be cleared to normalize to "ipd" +		 */ +		if (port & 0x800) +			port &= 0xff0; +		/* Node number is in AURA field, make it part of port # */ +		port |= (work->word0.pki.aura >> 10) << 12; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		port = work->word2.s_cn68xx.port; +	} else { +		port = work->word1.cn38xx.ipprt; +	} + +	return port; +} + +static inline void cvmx_wqe_set_port(cvmx_wqe_t *work, int port) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		work->word0.pki.channel = port; +	else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) +		work->word2.s_cn68xx.port = port; +	else +		work->word1.cn38xx.ipprt = port; +} + +static inline int cvmx_wqe_get_grp(cvmx_wqe_t *work) +{ +	int grp; + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		/* legacy: GRP[0..2] :=QOS */ +		grp = (0xff & work->word1.cn78xx.grp) >> 3; +	else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) +		grp = work->word1.cn68xx.grp; +	else +		grp = work->word1.cn38xx.grp; + +	return grp; +} + +static inline void cvmx_wqe_set_xgrp(cvmx_wqe_t *work, int grp) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		work->word1.cn78xx.grp = grp; +	else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) +		work->word1.cn68xx.grp = grp; +	else +		work->word1.cn38xx.grp = grp; +} + +static inline int cvmx_wqe_get_xgrp(cvmx_wqe_t *work) +{ +	int grp; + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		grp = work->word1.cn78xx.grp; +	else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) +		grp = work->word1.cn68xx.grp; +	else +		grp = work->word1.cn38xx.grp; + +	return grp; +} + +static inline void cvmx_wqe_set_grp(cvmx_wqe_t *work, int grp) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		unsigned int node = cvmx_get_node_num(); +		/* Legacy: GRP[0..2] :=QOS */ +		work->word1.cn78xx.grp &= 0x7; +		work->word1.cn78xx.grp |= 0xff & (grp << 3); +		work->word1.cn78xx.grp |= (node << 8); +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		work->word1.cn68xx.grp = grp; +	} else { +		work->word1.cn38xx.grp = grp; +	} +} + +static inline int cvmx_wqe_get_qos(cvmx_wqe_t *work) +{ +	int qos; + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		/* Legacy: GRP[0..2] :=QOS */ +		qos = work->word1.cn78xx.grp & 0x7; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		qos = work->word1.cn68xx.qos; +	} else { +		qos = work->word1.cn38xx.qos; +	} + +	return qos; +} + +static inline void cvmx_wqe_set_qos(cvmx_wqe_t *work, int qos) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		/* legacy: GRP[0..2] :=QOS */ +		work->word1.cn78xx.grp &= ~0x7; +		work->word1.cn78xx.grp |= qos & 0x7; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		work->word1.cn68xx.qos = qos; +	} else { +		work->word1.cn38xx.qos = qos; +	} +} + +static inline int cvmx_wqe_get_len(cvmx_wqe_t *work) +{ +	int len; + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		len = work->word1.cn78xx.len; +	else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) +		len = work->word1.cn68xx.len; +	else +		len = work->word1.cn38xx.len; + +	return len; +} + +static inline void cvmx_wqe_set_len(cvmx_wqe_t *work, int len) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		work->word1.cn78xx.len = len; +	else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) +		work->word1.cn68xx.len = len; +	else +		work->word1.cn38xx.len = len; +} + +/** + * This function returns, if there was L2/L1 errors detected in packet. + * + * @param work	pointer to work queue entry + * + * @return	0 if packet had no error, non-zero to indicate error code. + * + * Please refer to HRM for the specific model for full enumaration of error codes. + * With Octeon1/Octeon2 models, the returned code indicates L1/L2 errors. + * On CN73XX/CN78XX, the return code is the value of PKI_OPCODE_E, + * if it is non-zero, otherwise the returned code will be derived from + * PKI_ERRLEV_E such that an error indicated in LayerA will return 0x20, + * LayerB - 0x30, LayerC - 0x40 and so forth. + */ +static inline int cvmx_wqe_get_rcv_err(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		if (wqe->word2.err_level == CVMX_PKI_ERRLEV_E_RE || wqe->word2.err_code != 0) +			return wqe->word2.err_code; +		else +			return (wqe->word2.err_level << 4) + 0x10; +	} else if (work->word2.snoip.rcv_error) { +		return work->word2.snoip.err_code; +	} + +	return 0; +} + +static inline u32 cvmx_wqe_get_tag(cvmx_wqe_t *work) +{ +	return work->word1.tag; +} + +static inline void cvmx_wqe_set_tag(cvmx_wqe_t *work, u32 tag) +{ +	work->word1.tag = tag; +} + +static inline int cvmx_wqe_get_tt(cvmx_wqe_t *work) +{ +	return work->word1.tag_type; +} + +static inline void cvmx_wqe_set_tt(cvmx_wqe_t *work, int tt) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		work->word1.cn78xx.tag_type = (cvmx_pow_tag_type_t)tt; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		work->word1.cn68xx.tag_type = (cvmx_pow_tag_type_t)tt; +		work->word1.cn68xx.zero_2 = 0; +	} else { +		work->word1.cn38xx.tag_type = (cvmx_pow_tag_type_t)tt; +		work->word1.cn38xx.zero_2 = 0; +	} +} + +static inline u8 cvmx_wqe_get_unused8(cvmx_wqe_t *work) +{ +	u8 bits; + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		bits = wqe->word2.rsvd_0; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		bits = work->word0.pip.cn68xx.unused1; +	} else { +		bits = work->word0.pip.cn38xx.unused; +	} + +	return bits; +} + +static inline void cvmx_wqe_set_unused8(cvmx_wqe_t *work, u8 v) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		wqe->word2.rsvd_0 = v; +	} else if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) { +		work->word0.pip.cn68xx.unused1 = v; +	} else { +		work->word0.pip.cn38xx.unused = v; +	} +} + +static inline u8 cvmx_wqe_get_user_flags(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		return work->word0.pki.rsvd_2; +	else +		return 0; +} + +static inline void cvmx_wqe_set_user_flags(cvmx_wqe_t *work, u8 v) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		work->word0.pki.rsvd_2 = v; +} + +static inline int cvmx_wqe_get_channel(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		return (work->word0.pki.channel); +	else +		return cvmx_wqe_get_port(work); +} + +static inline void cvmx_wqe_set_channel(cvmx_wqe_t *work, int channel) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		work->word0.pki.channel = channel; +	else +		debug("%s: ERROR: not supported for model\n", __func__); +} + +static inline int cvmx_wqe_get_aura(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		return (work->word0.pki.aura); +	else +		return (work->packet_ptr.s.pool); +} + +static inline void cvmx_wqe_set_aura(cvmx_wqe_t *work, int aura) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		work->word0.pki.aura = aura; +	else +		work->packet_ptr.s.pool = aura; +} + +static inline int cvmx_wqe_get_style(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		return (work->word0.pki.style); +	return 0; +} + +static inline void cvmx_wqe_set_style(cvmx_wqe_t *work, int style) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) +		work->word0.pki.style = style; +} + +static inline int cvmx_wqe_is_l3_ip(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; +		/* Match all 4 values for v4/v6 with.without options */ +		if ((wqe->word2.lc_hdr_type & 0x1c) == CVMX_PKI_LTYPE_E_IP4) +			return 1; +		if ((wqe->word2.le_hdr_type & 0x1c) == CVMX_PKI_LTYPE_E_IP4) +			return 1; +		return 0; +	} else { +		return !work->word2.s_cn38xx.not_IP; +	} +} + +static inline int cvmx_wqe_is_l3_ipv4(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; +		/* Match 2 values - with/wotuout options */ +		if ((wqe->word2.lc_hdr_type & 0x1e) == CVMX_PKI_LTYPE_E_IP4) +			return 1; +		if ((wqe->word2.le_hdr_type & 0x1e) == CVMX_PKI_LTYPE_E_IP4) +			return 1; +		return 0; +	} else { +		return (!work->word2.s_cn38xx.not_IP && +			!work->word2.s_cn38xx.is_v6); +	} +} + +static inline int cvmx_wqe_is_l3_ipv6(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; +		/* Match 2 values - with/wotuout options */ +		if ((wqe->word2.lc_hdr_type & 0x1e) == CVMX_PKI_LTYPE_E_IP6) +			return 1; +		if ((wqe->word2.le_hdr_type & 0x1e) == CVMX_PKI_LTYPE_E_IP6) +			return 1; +		return 0; +	} else { +		return (!work->word2.s_cn38xx.not_IP && +			work->word2.s_cn38xx.is_v6); +	} +} + +static inline bool cvmx_wqe_is_l4_udp_or_tcp(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		if (wqe->word2.lf_hdr_type == CVMX_PKI_LTYPE_E_TCP) +			return true; +		if (wqe->word2.lf_hdr_type == CVMX_PKI_LTYPE_E_UDP) +			return true; +		return false; +	} + +	if (work->word2.s_cn38xx.not_IP) +		return false; + +	return (work->word2.s_cn38xx.tcp_or_udp != 0); +} + +static inline int cvmx_wqe_is_l2_bcast(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		return wqe->word2.is_l2_bcast; +	} else { +		return work->word2.s_cn38xx.is_bcast; +	} +} + +static inline int cvmx_wqe_is_l2_mcast(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		return wqe->word2.is_l2_mcast; +	} else { +		return work->word2.s_cn38xx.is_mcast; +	} +} + +static inline void cvmx_wqe_set_l2_bcast(cvmx_wqe_t *work, bool bcast) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		wqe->word2.is_l2_bcast = bcast; +	} else { +		work->word2.s_cn38xx.is_bcast = bcast; +	} +} + +static inline void cvmx_wqe_set_l2_mcast(cvmx_wqe_t *work, bool mcast) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		wqe->word2.is_l2_mcast = mcast; +	} else { +		work->word2.s_cn38xx.is_mcast = mcast; +	} +} + +static inline int cvmx_wqe_is_l3_bcast(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		return wqe->word2.is_l3_bcast; +	} +	debug("%s: ERROR: not supported for model\n", __func__); +	return 0; +} + +static inline int cvmx_wqe_is_l3_mcast(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		return wqe->word2.is_l3_mcast; +	} +	debug("%s: ERROR: not supported for model\n", __func__); +	return 0; +} + +/** + * This function returns is there was IP error detected in packet. + * For 78XX it does not flag ipv4 options and ipv6 extensions. + * For older chips if PIP_GBL_CTL was proviosned to flag ip4_otions and + * ipv6 extension, it will be flag them. + * @param work	pointer to work queue entry + * @return	1 -- If IP error was found in packet + *          0 -- If no IP error was found in packet. + */ +static inline int cvmx_wqe_is_ip_exception(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		if (wqe->word2.err_level == CVMX_PKI_ERRLEV_E_LC) +			return 1; +		else +			return 0; +	} + +	return work->word2.s.IP_exc; +} + +static inline int cvmx_wqe_is_l4_error(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		if (wqe->word2.err_level == CVMX_PKI_ERRLEV_E_LF) +			return 1; +		else +			return 0; +	} else { +		return work->word2.s.L4_error; +	} +} + +static inline void cvmx_wqe_set_vlan(cvmx_wqe_t *work, bool set) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		wqe->word2.vlan_valid = set; +	} else { +		work->word2.s.vlan_valid = set; +	} +} + +static inline int cvmx_wqe_is_vlan(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		return wqe->word2.vlan_valid; +	} else { +		return work->word2.s.vlan_valid; +	} +} + +static inline int cvmx_wqe_is_vlan_stacked(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		return wqe->word2.vlan_stacked; +	} else { +		return work->word2.s.vlan_stacked; +	} +} + +/** + * Extract packet data buffer pointer from work queue entry. + * + * Returns the legacy (Octeon1/Octeon2) buffer pointer structure + * for the linked buffer list. + * On CN78XX, the native buffer pointer structure is converted into + * the legacy format. + * The legacy buf_ptr is then stored in the WQE, and word0 reserved + * field is set to indicate that the buffer pointers were translated. + * If the packet data is only found inside the work queue entry, + * a standard buffer pointer structure is created for it. + */ +cvmx_buf_ptr_t cvmx_wqe_get_packet_ptr(cvmx_wqe_t *work); + +static inline int cvmx_wqe_get_bufs(cvmx_wqe_t *work) +{ +	int bufs; + +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		bufs = work->word0.pki.bufs; +	} else { +		/* Adjust for packet-in-WQE cases */ +		if (cvmx_unlikely(work->word2.s_cn38xx.bufs == 0 && !work->word2.s.software)) +			(void)cvmx_wqe_get_packet_ptr(work); +		bufs = work->word2.s_cn38xx.bufs; +	} +	return bufs; +} + +/** + * Free Work Queue Entry memory + * + * Will return the WQE buffer to its pool, unless the WQE contains + * non-redundant packet data. + * This function is intended to be called AFTER the packet data + * has been passed along to PKO for transmission and release. + * It can also follow a call to cvmx_helper_free_packet_data() + * to release the WQE after associated data was released. + */ +void cvmx_wqe_free(cvmx_wqe_t *work); + +/** + * Check if a work entry has been intiated by software + * + */ +static inline bool cvmx_wqe_is_soft(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		return wqe->word2.software; +	} else { +		return work->word2.s.software; +	} +} + +/** + * Allocate a work-queue entry for delivering software-initiated + * event notifications. + * The application data is copied into the work-queue entry, + * if the space is sufficient. + */ +cvmx_wqe_t *cvmx_wqe_soft_create(void *data_p, unsigned int data_sz); + +/* Errata (PKI-20776) PKI_BUFLINK_S's are endian-swapped + * CN78XX pass 1.x has a bug where the packet pointer in each segment is + * written in the opposite endianness of the configured mode. Fix these here. + */ +static inline void cvmx_wqe_pki_errata_20776(cvmx_wqe_t *work) +{ +	cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +	if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X) && !wqe->pki_errata20776) { +		u64 bufs; +		cvmx_buf_ptr_pki_t buffer_next; + +		bufs = wqe->word0.bufs; +		buffer_next = wqe->packet_ptr; +		while (bufs > 1) { +			cvmx_buf_ptr_pki_t next; +			void *nextaddr = cvmx_phys_to_ptr(buffer_next.addr - 8); + +			memcpy(&next, nextaddr, sizeof(next)); +			next.u64 = __builtin_bswap64(next.u64); +			memcpy(nextaddr, &next, sizeof(next)); +			buffer_next = next; +			bufs--; +		} +		wqe->pki_errata20776 = 1; +	} +} + +/** + * @INTERNAL + * + * Extract the native PKI-specific buffer pointer from WQE. + * + * NOTE: Provisional, may be superceded. + */ +static inline cvmx_buf_ptr_pki_t cvmx_wqe_get_pki_pkt_ptr(cvmx_wqe_t *work) +{ +	cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +	if (!octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_buf_ptr_pki_t x = { 0 }; +		return x; +	} + +	cvmx_wqe_pki_errata_20776(work); +	return wqe->packet_ptr; +} + +/** + * Set the buffer segment count for a packet. + * + * @return Returns the actual resulting value in the WQE fielda + * + */ +static inline unsigned int cvmx_wqe_set_bufs(cvmx_wqe_t *work, unsigned int bufs) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		work->word0.pki.bufs = bufs; +		return work->word0.pki.bufs; +	} + +	work->word2.s.bufs = bufs; +	return work->word2.s.bufs; +} + +/** + * Get the offset of Layer-3 header, + * only supported when Layer-3 protocol is IPv4 or IPv6. + * + * @return Returns the offset, or 0 if the offset is not known or unsupported. + * + * FIXME: Assuming word4 is present. + */ +static inline unsigned int cvmx_wqe_get_l3_offset(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; +		/* Match 4 values: IPv4/v6 w/wo options */ +		if ((wqe->word2.lc_hdr_type & 0x1c) == CVMX_PKI_LTYPE_E_IP4) +			return wqe->word4.ptr_layer_c; +	} else { +		return work->word2.s.ip_offset; +	} + +	return 0; +} + +/** + * Set the offset of Layer-3 header in a packet. + * Typically used when an IP packet is generated by software + * or when the Layer-2 header length is modified, and + * a subsequent recalculation of checksums is anticipated. + * + * @return Returns the actual value of the work entry offset field. + * + * FIXME: Assuming word4 is present. + */ +static inline unsigned int cvmx_wqe_set_l3_offset(cvmx_wqe_t *work, unsigned int ip_off) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; +		/* Match 4 values: IPv4/v6 w/wo options */ +		if ((wqe->word2.lc_hdr_type & 0x1c) == CVMX_PKI_LTYPE_E_IP4) +			wqe->word4.ptr_layer_c = ip_off; +	} else { +		work->word2.s.ip_offset = ip_off; +	} + +	return cvmx_wqe_get_l3_offset(work); +} + +/** + * Set the indication that the packet contains a IPv4 Layer-3 * header. + * Use 'cvmx_wqe_set_l3_ipv6()' if the protocol is IPv6. + * When 'set' is false, the call will result in an indication + * that the Layer-3 protocol is neither IPv4 nor IPv6. + * + * FIXME: Add IPV4_OPT handling based on L3 header length. + */ +static inline void cvmx_wqe_set_l3_ipv4(cvmx_wqe_t *work, bool set) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		if (set) +			wqe->word2.lc_hdr_type = CVMX_PKI_LTYPE_E_IP4; +		else +			wqe->word2.lc_hdr_type = CVMX_PKI_LTYPE_E_NONE; +	} else { +		work->word2.s.not_IP = !set; +		if (set) +			work->word2.s_cn38xx.is_v6 = 0; +	} +} + +/** + * Set packet Layer-3 protocol to IPv6. + * + * FIXME: Add IPV6_OPT handling based on presence of extended headers. + */ +static inline void cvmx_wqe_set_l3_ipv6(cvmx_wqe_t *work, bool set) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		if (set) +			wqe->word2.lc_hdr_type = CVMX_PKI_LTYPE_E_IP6; +		else +			wqe->word2.lc_hdr_type = CVMX_PKI_LTYPE_E_NONE; +	} else { +		work->word2.s_cn38xx.not_IP = !set; +		if (set) +			work->word2.s_cn38xx.is_v6 = 1; +	} +} + +/** + * Set a packet Layer-4 protocol type to UDP. + */ +static inline void cvmx_wqe_set_l4_udp(cvmx_wqe_t *work, bool set) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		if (set) +			wqe->word2.lf_hdr_type = CVMX_PKI_LTYPE_E_UDP; +		else +			wqe->word2.lf_hdr_type = CVMX_PKI_LTYPE_E_NONE; +	} else { +		if (!work->word2.s_cn38xx.not_IP) +			work->word2.s_cn38xx.tcp_or_udp = set; +	} +} + +/** + * Set a packet Layer-4 protocol type to TCP. + */ +static inline void cvmx_wqe_set_l4_tcp(cvmx_wqe_t *work, bool set) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		if (set) +			wqe->word2.lf_hdr_type = CVMX_PKI_LTYPE_E_TCP; +		else +			wqe->word2.lf_hdr_type = CVMX_PKI_LTYPE_E_NONE; +	} else { +		if (!work->word2.s_cn38xx.not_IP) +			work->word2.s_cn38xx.tcp_or_udp = set; +	} +} + +/** + * Set the "software" flag in a work entry. + */ +static inline void cvmx_wqe_set_soft(cvmx_wqe_t *work, bool set) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		wqe->word2.software = set; +	} else { +		work->word2.s.software = set; +	} +} + +/** + * Return true if the packet is an IP fragment. + */ +static inline bool cvmx_wqe_is_l3_frag(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		return (wqe->word2.is_frag != 0); +	} + +	if (!work->word2.s_cn38xx.not_IP) +		return (work->word2.s.is_frag != 0); + +	return false; +} + +/** + * Set the indicator that the packet is an fragmented IP packet. + */ +static inline void cvmx_wqe_set_l3_frag(cvmx_wqe_t *work, bool set) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		wqe->word2.is_frag = set; +	} else { +		if (!work->word2.s_cn38xx.not_IP) +			work->word2.s.is_frag = set; +	} +} + +/** + * Set the packet Layer-3 protocol to RARP. + */ +static inline void cvmx_wqe_set_l3_rarp(cvmx_wqe_t *work, bool set) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		if (set) +			wqe->word2.lc_hdr_type = CVMX_PKI_LTYPE_E_RARP; +		else +			wqe->word2.lc_hdr_type = CVMX_PKI_LTYPE_E_NONE; +	} else { +		work->word2.snoip.is_rarp = set; +	} +} + +/** + * Set the packet Layer-3 protocol to ARP. + */ +static inline void cvmx_wqe_set_l3_arp(cvmx_wqe_t *work, bool set) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		if (set) +			wqe->word2.lc_hdr_type = CVMX_PKI_LTYPE_E_ARP; +		else +			wqe->word2.lc_hdr_type = CVMX_PKI_LTYPE_E_NONE; +	} else { +		work->word2.snoip.is_arp = set; +	} +} + +/** + * Return true if the packet Layer-3 protocol is ARP. + */ +static inline bool cvmx_wqe_is_l3_arp(cvmx_wqe_t *work) +{ +	if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { +		cvmx_wqe_78xx_t *wqe = (cvmx_wqe_78xx_t *)work; + +		return (wqe->word2.lc_hdr_type == CVMX_PKI_LTYPE_E_ARP); +	} + +	if (work->word2.s_cn38xx.not_IP) +		return (work->word2.snoip.is_arp != 0); + +	return false; +} + +#endif /* __CVMX_WQE_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/octeon_eth.h b/arch/mips/mach-octeon/include/mach/octeon_eth.h new file mode 100644 index 00000000000..bfef0a6e9f1 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/octeon_eth.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + */ + +#ifndef __OCTEON_ETH_H__ +#define __OCTEON_ETH_H__ + +#include <phy.h> +#include <miiphy.h> + +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/octeon_fdt.h> + +struct eth_device; + +/** Ethernet device private data structure for octeon ethernet */ +struct octeon_eth_info { +	u64 link_state; +	u32 port;		   /** ipd port */ +	u32 interface;		   /** Port interface */ +	u32 index;		   /** port index on interface */ +	int node;		   /** OCX node number */ +	u32 initted_flag;	   /** 0 if port not initialized */ +	struct mii_dev *mii_bus;   /** MII bus for PHY */ +	struct phy_device *phydev; /** PHY device */ +	struct eth_device *ethdev; /** Eth device this priv is part of */ +	int mii_addr; +	int phy_fdt_offset;		    /** Offset of PHY info in device tree */ +	int fdt_offset;			    /** Offset of Eth interface in DT */ +	int phy_offset;			    /** Offset of PHY dev in device tree */ +	enum cvmx_phy_type phy_device_type; /** Type of PHY */ +	/* current link status, use to reconfigure on status changes */ +	u64 packets_sent; +	u64 packets_received; +	u32 link_speed : 2; +	u32 link_duplex : 1; +	u32 link_status : 1; +	u32 loopback : 1; +	u32 enabled : 1; +	u32 is_c45 : 1;		    /** Set if we need to use clause 45 */ +	u32 vitesse_sfp_config : 1; /** Need Vitesse SFP config */ +	u32 ti_gpio_config : 1;	    /** Need TI GPIO config */ +	u32 bgx_mac_set : 1;	    /** Has the BGX MAC been set already */ +	u64 last_bgx_mac;	    /** Last BGX MAC address set */ +	u64 gmx_base;		    /** Base address to access GMX CSRs */ +	bool mod_abs;		    /** True if module is absent */ + +	/** +	 * User defined function to check if a SFP+ module is absent or not. +	 * +	 * @param	dev	Ethernet device +	 * @param	data	User supplied data +	 */ +	int (*check_mod_abs)(struct eth_device *dev, void *data); + +	/** User supplied data for check_mod_abs */ +	void *mod_abs_data; +	/** +	 * Called to check the status of a port.  This is used for some +	 * Vitesse and Inphi phys to probe the sFP adapter. +	 */ +	int (*phy_port_check)(struct phy_device *dev); +	/** +	 * Called whenever mod_abs changes state +	 * +	 * @param	dev	Ethernet device +	 * @param	mod_abs	True if module is absent +	 * +	 * @return	0 for success, otherwise error +	 */ +	int (*mod_abs_changed)(struct eth_device *dev, bool mod_abs); +	/** SDK phy information data structure */ +	cvmx_phy_info_t phy_info; +#ifdef CONFIG_OCTEON_SFP +	/** Information about connected SFP/SFP+/SFP28/QSFP+/QSFP28 module */ +	struct octeon_sfp_info sfp; +#endif +}; + +/** + * Searches for an ethernet device based on interface and index. + * + * @param interface - interface number to search for + * @param index - index to search for + * + * @returns pointer to ethernet device or NULL if not found. + */ +struct eth_device *octeon_find_eth_by_interface_index(int interface, int index); + +/** + * User-defined function called when the link state changes + * + * @param[in]	dev		Ethernet device + * @param	link_state	new link state + * + * NOTE: This is defined as a weak function. + */ +void board_net_set_link(struct eth_device *dev, cvmx_helper_link_info_t link_state); + +/** + * Registers a function to be called when the link goes down.  The function is + * often used for things like reading the SFP+ EEPROM. + * + * @param	dev		Ethernet device + * @param	phy_port_check	Function to call + */ +void octeon_eth_register_phy_port_check(struct eth_device *dev, +					int (*phy_port_check)(struct phy_device *dev)); + +/** + * This weak function is called after the phy driver is connected but before + * it is initialized. + * + * @param	dev	Ethernet device for phy + * + * @return	0 to continue, or -1 for error to stop setting up the phy + */ +int octeon_eth_board_post_setup_phy(struct eth_device *dev); + +/** + * Registers a function to be called whenever a mod_abs change is detected. + * + * @param	dev		Ethernet device + * @param	mod_abs_changed	Function to be called + */ +void octeon_eth_register_mod_abs_changed(struct eth_device *dev, +					 int (*mod_abs_changed)(struct eth_device *dev, +								bool mod_abs)); + +/** + * Checks for state changes with the link state or module state + * + * @param	dev	Ethernet device to check + * + * NOTE: If the module state is changed then the module callback is called. + */ +void octeon_phy_port_check(struct eth_device *dev); + +#endif /* __OCTEON_ETH_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/octeon_fdt.h b/arch/mips/mach-octeon/include/mach/octeon_fdt.h new file mode 100644 index 00000000000..31878cb233d --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/octeon_fdt.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + */ + +#ifndef __OCTEON_FDT_H__ +#define __OCTEON_FDT_H__ + +struct phy_device; + +/** Type of GPIO pin */ +enum octeon_gpio_type { +	GPIO_TYPE_OCTEON,  /** Native Octeon */ +	GPIO_TYPE_PCA953X, /** PCA953X i2c GPIO expander */ +	GPIO_TYPE_PCA9554, /** PCA9554 i2c GPIO expander */ +	GPIO_TYPE_PCA9555, /** PCA9555 i2c GPIO expander */ +	GPIO_TYPE_PCA9698, /** PCA9698 i2c GPIO expander */ +#ifdef CONFIG_PHY_VITESSE +	GPIO_TYPE_VSC8488, /** Vitesse VSC8488 or related PHY GPIO */ +#endif +	GPIO_TYPE_UNKNOWN /** Unknown GPIO type */ +}; + +/** + * Trims nodes from the flat device tree. + * + * @param fdt - pointer to working FDT, usually in gd->fdt_blob + * @param fdt_key - key to preserve.  All non-matching keys are removed + * @param trim_name - name of property to look for.  If NULL use + *		      'cavium,qlm-trim' + * @param rename - set to TRUE to rename interfaces. + * @param callback - function to call on matched nodes. + * @param cbarg - passed to callback. + * + * The key should look something like device #, type where device # is a + * number from 0-9 and type is a string describing the type.  For QLM + * operations this would typically contain the QLM number followed by + * the type in the device tree, like "0,xaui", "0,sgmii", etc.  This function + * will trim all items in the device tree which match the device number but + * have a type which does not match.  For example, if a QLM has a xaui module + * installed on QLM 0 and "0,xaui" is passed as a key, then all FDT nodes that + * have "0,xaui" will be preserved but all others, i.e. "0,sgmii" will be + * removed. + * + * Note that the trim_name must also match.  If trim_name is NULL then it + * looks for the property "cavium,qlm-trim". + * + * Also, when the trim_name is "cavium,qlm-trim" or NULL that the interfaces + * will also be renamed based on their register values. + * + * For example, if a PIP interface is named "interface@W" and has the property + * reg = <0> then the interface will be renamed after this function to + * interface@0. + * + * @return 0 for success. + */ +int octeon_fdt_patch_rename(void *fdt, const char *fdt_key, const char *trim_name, bool rename, +			    void (*callback)(void *fdt, int offset, void *arg), void *cbarg); + +/** + * Trims nodes from the flat device tree. + * + * @param fdt - pointer to working FDT, usually in gd->fdt_blob + * @param fdt_key - key to preserve.  All non-matching keys are removed + * @param trim_name - name of property to look for.  If NULL use + *		      'cavium,qlm-trim' + * + * The key should look something like device #, type where device # is a + * number from 0-9 and type is a string describing the type.  For QLM + * operations this would typically contain the QLM number followed by + * the type in the device tree, like "0,xaui", "0,sgmii", etc.  This function + * will trim all items in the device tree which match the device number but + * have a type which does not match.  For example, if a QLM has a xaui module + * installed on QLM 0 and "0,xaui" is passed as a key, then all FDT nodes that + * have "0,xaui" will be preserved but all others, i.e. "0,sgmii" will be + * removed. + * + * Note that the trim_name must also match.  If trim_name is NULL then it + * looks for the property "cavium,qlm-trim". + * + * Also, when the trim_name is "cavium,qlm-trim" or NULL that the interfaces + * will also be renamed based on their register values. + * + * For example, if a PIP interface is named "interface@W" and has the property + * reg = <0> then the interface will be renamed after this function to + * interface@0. + * + * @return 0 for success. + */ +int octeon_fdt_patch(void *fdt, const char *fdt_key, const char *trim_name); + +/** + * Fix up the MAC address in the flat device tree based on the MAC address + * stored in ethaddr or in the board descriptor. + * + * NOTE: This function is weak and an alias for __octeon_fixup_fdt_mac_addr. + */ +void octeon_fixup_fdt_mac_addr(void); + +/** + * This function fixes the clock-frequency in the flat device tree for the UART. + * + * NOTE: This function is weak and an alias for __octeon_fixup_fdt_uart. + */ +void octeon_fixup_fdt_uart(void); + +/** + * This function fills in the /memory portion of the flat device tree. + * + * NOTE: This function is weak and aliased to __octeon_fixup_fdt_memory. + */ +void octeon_fixup_fdt_memory(void); + +int board_fixup_fdt(void); + +void octeon_fixup_fdt(void); + +/** + * This is a helper function to find the offset of a PHY device given + * an Ethernet device. + * + * @param[in] eth - Ethernet device to search for PHY offset + * + * @returns offset of phy info in device tree or -1 if not found + */ +int octeon_fdt_find_phy(const struct udevice *eth); + +/** + * This helper function returns if a node contains the specified vendor name. + * + * @param[in]	fdt		pointer to device tree blob + * @param	nodeoffset	offset of the tree node + * @param[in]	vendor		name of vendor to check + * + * returns: + *	0, if the node has a compatible vendor string property + *	1, if the node does not contain the vendor string property + *	-FDT_ERR_NOTFOUND, if the given node has no 'compatible' property + *	-FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag + *	-FDT_ERR_BADMAGIC, + *	-FDT_ERR_BADVERSION, + *	-FDT_BADSTATE, + *	-FDT_ERR_BADSTRUCTURE, standard meanings + */ +int octeon_fdt_compat_vendor(const void *fdt, int nodeoffset, const char *vendor); + +/** + * Given a node in the device tree get the OCTEON OCX node number + * + * @param fdt		pointer to flat device tree + * @param nodeoffset	node offset to get OCX node for + * + * @return the Octeon OCX node number + */ +int octeon_fdt_get_soc_node(const void *fdt, int nodeoffset); + +/** + * Given a FDT node, check if it is compatible with a list of devices + * + * @param[in]	fdt		Flat device tree pointer + * @param	node_offset	Node offset in device tree + * @param[in]	strlist		Array of FDT devices to check, end must be NULL + * + * @return	0 if at least one device is compatible, 1 if not compatible. + */ +int octeon_fdt_node_check_compatible(const void *fdt, int node_offset, const char *const *strlist); +/** + * Given a node offset, find the i2c bus number for that node + * + * @param[in]	fdt	Pointer to flat device tree + * @param	node_offset	Node offset in device tree + * + * @return	i2c bus number or -1 if error + */ +int octeon_fdt_i2c_get_bus(const void *fdt, int node_offset); + +/** + * Given an offset into the fdt, output the i2c bus and address of the device + * + * @param[in]	fdt	fdt blob pointer + * @param	node	offset in FDT of device + * @param[out]	bus	i2c bus number of device + * @param[out]	addr	address of device on i2c bus + * + * @return	0 for success, -1 on error + */ +int octeon_fdt_get_i2c_bus_addr(const void *fdt, int node, int *bus, int *addr); + +/** + * Reads a GPIO pin given the node of the GPIO device in the device tree and + * the pin number. + * + * @param[in]	fdt	fdt blob pointer + * @param	phandle	phandle of GPIO node + * @param	pin	pin number to read + * + * @return	0 = pin is low, 1 = pin is high, -1 = error + */ +int octeon_fdt_read_gpio(const void *fdt, int phandle, int pin); + +/** + * Reads a GPIO pin given the node of the GPIO device in the device tree and + * the pin number. + * + * @param[in]	fdt	fdt blob pointer + * @param	phandle	phandle of GPIO node + * @param	pin	pin number to read + * @param	val	value to write (1 = high, 0 = low) + * + * @return	0 = success, -1 = error + */ +int octeon_fdt_set_gpio(const void *fdt, int phandle, int pin, int val); + +/** + * Given the node to a MAC entry in the device tree, output the i2c bus, address + * and if the module is absent. + * + * @param[in]	fdt		flat device tree pointer + * @param	mac_node	node of Ethernet port in the FDT + * @param[out]	bus		i2c bus address of SFP EEPROM + * @param[out]	addr		i2c address of SFP EEPROM + * @param[out]	mod_abs		Set true if module is absent, false if present + * + * @return	0 for success, -1 if there are problems with the device tree + */ +int octeon_fdt_get_sfp_eeprom(const void *fdt, int mac_node, int *bus, int *addr, bool *mod_abs); + +/** + * Given a node to a MAC entry in the device tree, output the i2c bus, address + * and if the module is absent + * + * @param[in]	fdt		flat device tree pointer + * @param	mac_node	node of QSFP Ethernet port in FDT + * @param[out]	bus		i2c bus address of SFP EEPROM + * @param[out]	addr		i2c address of SFP eeprom + * @param[out]	mod_abs		Set true if module is absent, false if present + * + * @return	0 for success, -1 if there are problems with the device tree + */ +int octeon_fdt_get_qsfp_eeprom(const void *fdt, int mac_node, int *bus, int *addr, bool *mod_abs); + +/** + * Given the node of a GPIO entry output the GPIO type, i2c bus and i2c + * address. + * + * @param	fdt_node	node of GPIO in device tree, generally + *				derived from a phandle. + * @param[out]	type		Type of GPIO detected + * @param[out]	i2c_bus		For i2c GPIO expanders, the i2c bus number + * @param[out]	i2c_addr	For i2c GPIO expanders, the i2c address + * + * @return	0 for success, -1 for errors + * + * NOTE: It is up to the caller to determine the pin number. + */ +int octeon_fdt_get_gpio_info(int fdt_node, enum octeon_gpio_type *type, int *i2c_bus, +			     int *i2c_addr); + +/** + * Get the PHY data structure for the specified FDT node and output the type + * + * @param	fdt_node	FDT node of phy + * @param[out]	type		Type of GPIO + * + * @return	pointer to phy device or NULL if no match found. + */ +struct phy_device *octeon_fdt_get_phy_gpio_info(int fdt_node, enum octeon_gpio_type *type); +#endif /* __OCTEON_FDT_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/octeon_pci.h b/arch/mips/mach-octeon/include/mach/octeon_pci.h new file mode 100644 index 00000000000..3034f23dc65 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/octeon_pci.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + */ + +#ifndef __OCTEON_PCI_H__ +#define __OCTEON_PCI_H__ + +/** + * EEPROM entry struct + */ +union octeon_pcie_eeprom { +	u64 u64; +	struct octeon_data_s { +		/** +		 * 0x9DA1 valid entry, 0x6A5D end of table, 0xffff invalid +		 * access +		 */ +		u64 preamble : 16; +u64: 1; /** Reserved */ +		/** Physical function number accessed by the write operation. */ +		u64 pf : 2; +		/** +		 * Specifies bit<31> of the address written by hardware. +		 * 1 = configuration mask register, 0 = configuration register +		 */ +		u64 cs2 : 1; +		/** +		 * Specifies bits<11:0> of the address written by hardware. +		 * Bits<30:12> of this address are all 0s. +		 */ +		u64 address : 12; +		u64 data : 32; +	} s; +}; + +void pci_dev_post_init(void); + +int octeon_pci_io_readb(unsigned int reg); +void octeon_pci_io_writeb(int value, unsigned int reg); +int octeon_pci_io_readw(unsigned int reg); +void octeon_pci_io_writew(int value, unsigned int reg); +int octeon_pci_io_readl(unsigned int reg); +void octeon_pci_io_writel(int value, unsigned int reg); +int octeon_pci_mem1_readb(unsigned int reg); +void octeon_pci_mem1_writeb(int value, unsigned int reg); +int octeon_pci_mem1_readw(unsigned int reg); +void octeon_pci_mem1_writew(int value, unsigned int reg); +int octeon_pci_mem1_readl(unsigned int reg); +void octeon_pci_mem1_writel(int value, unsigned int reg); + +/* In the TLB mapped case, these also work with virtual addresses, +** and do the required virt<->phys translations as well. */ +u32 octeon_pci_phys_to_bus(u32 phys); +u32 octeon_pci_bus_to_phys(u32 bus); + +/** + * Searches PCIe EEPROM for override data specified by address and pf. + * + * @param	address - PCIe config space address + * @param	pf	- PCIe config space pf num + * @param[out]	id	- override device and vendor ID + * + * @return	0 if override found, 1 if not found. + */ +int octeon_find_pcie_id_override(unsigned int address, unsigned int pf, u32 *id); + +#endif /* __OCTEON_PCI_H__ */ diff --git a/arch/mips/mach-octeon/include/mach/octeon_qlm.h b/arch/mips/mach-octeon/include/mach/octeon_qlm.h new file mode 100644 index 00000000000..219625b2568 --- /dev/null +++ b/arch/mips/mach-octeon/include/mach/octeon_qlm.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Marvell International Ltd. + */ + +#ifndef __OCTEON_QLM_H__ +#define __OCTEON_QLM_H__ + +/* Reference clock selector values for ref_clk_sel */ +#define OCTEON_QLM_REF_CLK_100MHZ 0 /** 100 MHz */ +#define OCTEON_QLM_REF_CLK_125MHZ 1 /** 125 MHz */ +#define OCTEON_QLM_REF_CLK_156MHZ 2 /** 156.25 MHz */ +#define OCTEON_QLM_REF_CLK_161MHZ 3 /** 161.1328125 MHz */ + +/** + * Configure qlm/dlm speed and mode. + * @param qlm     The QLM or DLM to configure + * @param speed   The speed the QLM needs to be configured in Mhz. + * @param mode    The QLM to be configured as SGMII/XAUI/PCIe. + * @param rc      Only used for PCIe, rc = 1 for root complex mode, 0 for EP + *		  mode. + * @param pcie_mode Only used when qlm/dlm are in pcie mode. + * @param ref_clk_sel Reference clock to use for 70XX where: + *			0: 100MHz + *			1: 125MHz + *			2: 156.25MHz + *			3: 161.1328125MHz (CN73XX and CN78XX only) + * @param ref_clk_input	This selects which reference clock input to use.  For + *			cn70xx: + *				0: DLMC_REF_CLK0 + *				1: DLMC_REF_CLK1 + *				2: DLM0_REF_CLK + *			cn61xx: (not used) + *			cn78xx/cn76xx/cn73xx: + *				0: Internal clock (QLM[0-7]_REF_CLK) + *				1: QLMC_REF_CLK0 + *				2: QLMC_REF_CLK1 + * + * @return	Return 0 on success or -1. + * + * @note	When the 161MHz clock is used it can only be used for + *		XLAUI mode with a 6316 speed or XFI mode with a 103125 speed. + *		This rate is also only supported for CN73XX and CN78XX. + */ +int octeon_configure_qlm(int qlm, int speed, int mode, int rc, int pcie_mode, int ref_clk_sel, +			 int ref_clk_input); + +int octeon_configure_qlm_cn78xx(int node, int qlm, int speed, int mode, int rc, int pcie_mode, +				int ref_clk_sel, int ref_clk_input); + +/** + * Some QLM speeds need to override the default tuning parameters + * + * @param node     Node to configure + * @param qlm      QLM to configure + * @param baud_mhz Desired speed in MHz + * @param lane     Lane the apply the tuning parameters + * @param tx_swing Voltage swing.  The higher the value the lower the voltage, + *		   the default value is 7. + * @param tx_pre   pre-cursor pre-emphasis + * @param tx_post  post-cursor pre-emphasis. + * @param tx_gain   Transmit gain. Range 0-7 + * @param tx_vboost Transmit voltage boost. Range 0-1 + */ +void octeon_qlm_tune_per_lane_v3(int node, int qlm, int baud_mhz, int lane, int tx_swing, +				 int tx_pre, int tx_post, int tx_gain, int tx_vboost); + +/** + * Some QLM speeds need to override the default tuning parameters + * + * @param node     Node to configure + * @param qlm      QLM to configure + * @param baud_mhz Desired speed in MHz + * @param tx_swing Voltage swing.  The higher the value the lower the voltage, + *		   the default value is 7. + * @param tx_premptap bits [0:3] pre-cursor pre-emphasis, bits[4:8] post-cursor + *		      pre-emphasis. + * @param tx_gain   Transmit gain. Range 0-7 + * @param tx_vboost Transmit voltage boost. Range 0-1 + */ +void octeon_qlm_tune_v3(int node, int qlm, int baud_mhz, int tx_swing, int tx_premptap, int tx_gain, +			int tx_vboost); + +/** + * Disables DFE for the specified QLM lane(s). + * This function should only be called for low-loss channels. + * + * @param node     Node to configure + * @param qlm      QLM to configure + * @param lane     Lane to configure, or -1 all lanes + * @param baud_mhz The speed the QLM needs to be configured in Mhz. + * @param mode     The QLM to be configured as SGMII/XAUI/PCIe. + */ +void octeon_qlm_dfe_disable(int node, int qlm, int lane, int baud_mhz, int mode); + +/** + * Some QLMs need to override the default pre-ctle for low loss channels. + * + * @param node     Node to configure + * @param qlm      QLM to configure + * @param pre_ctle pre-ctle settings for low loss channels + */ +void octeon_qlm_set_channel_v3(int node, int qlm, int pre_ctle); + +void octeon_init_qlm(int node); + +int octeon_mcu_probe(int node); + +#endif /* __OCTEON_QLM_H__ */ | 
