/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright 2017 NXP
 */

#ifndef CAAM_JR_HW_SPECIFIC_H
#define CAAM_JR_HW_SPECIFIC_H

#include <caam_jr_config.h>

/*
 * Offset to the registers of a job ring.
 * Is different for each job ring.
 */
#define CHAN_BASE(jr)   ((size_t)(jr)->register_base_addr)

#define SEC_JOB_RING_IS_FULL(pi, ci, ring_max_size, ring_threshold) \
		((((pi) + 1 + ((ring_max_size) - (ring_threshold))) & \
		  (ring_max_size - 1))  == ((ci)))

#define SEC_CIRCULAR_COUNTER(x, max)   (((x) + 1) & (max - 1))

/*
 * Assert that cond is true. If !cond is true, display str and the vararg list
 * in a printf-like syntax. also, if !cond is true, return altRet.
 *
 * \param cond          A boolean expression to be asserted true
 * \param altRet        The value to be returned if cond doesn't hold true
 * \param str           A quoted char string
 *
 * E.g.:
 *      SEC_ASSERT(ret > 0, 0, "ERROR initializing app: code = %d\n", ret);
 */
#define SEC_ASSERT(cond, altRet, ...) do {\
	if (unlikely(!(cond))) {\
		CAAM_JR_ERR(__VA_ARGS__); \
		return altRet; \
	} \
} while (0)

#define SEC_DP_ASSERT(cond, altRet, ...) do {\
	if (unlikely(!(cond))) {\
		CAAM_JR_DP_ERR(__VA_ARGS__); \
		return altRet; \
	} \
} while (0)

#define ASSERT(x)

/*
 * Constants representing various job ring registers
 */
#if CAAM_BYTE_ORDER == __BIG_ENDIAN
#define JR_REG_IRBA_OFFSET		0x0000
#define JR_REG_IRBA_OFFSET_LO		0x0004
#else
#define JR_REG_IRBA_OFFSET		0x0004
#define JR_REG_IRBA_OFFSET_LO		0x0000
#endif

#define JR_REG_IRSR_OFFSET		0x000C
#define JR_REG_IRSA_OFFSET		0x0014
#define JR_REG_IRJA_OFFSET		0x001C

#if CAAM_BYTE_ORDER == __BIG_ENDIAN
#define JR_REG_ORBA_OFFSET		0x0020
#define JR_REG_ORBA_OFFSET_LO		0x0024
#else
#define JR_REG_ORBA_OFFSET		0x0024
#define JR_REG_ORBA_OFFSET_LO		0x0020
#endif

#define JR_REG_ORSR_OFFSET		0x002C
#define JR_REG_ORJR_OFFSET		0x0034
#define JR_REG_ORSFR_OFFSET		0x003C
#define JR_REG_JROSR_OFFSET		0x0044
#define JR_REG_JRINT_OFFSET		0x004C

#define JR_REG_JRCFG_OFFSET		0x0050
#define JR_REG_JRCFG_OFFSET_LO		0x0054

#define JR_REG_IRRI_OFFSET		0x005C
#define JR_REG_ORWI_OFFSET		0x0064
#define JR_REG_JRCR_OFFSET		0x006C

/*
 * Constants for error handling on job ring
 */
#define JR_REG_JRINT_ERR_TYPE_SHIFT	8
#define JR_REG_JRINT_ERR_ORWI_SHIFT	16
#define JR_REG_JRINIT_JRE_SHIFT		1

#define JRINT_JRE			(1 << JR_REG_JRINIT_JRE_SHIFT)
#define JRINT_ERR_WRITE_STATUS		(1 << JR_REG_JRINT_ERR_TYPE_SHIFT)
#define JRINT_ERR_BAD_INPUT_BASE	(3 << JR_REG_JRINT_ERR_TYPE_SHIFT)
#define JRINT_ERR_BAD_OUTPUT_BASE	(4 << JR_REG_JRINT_ERR_TYPE_SHIFT)
#define JRINT_ERR_WRITE_2_IRBA		(5 << JR_REG_JRINT_ERR_TYPE_SHIFT)
#define JRINT_ERR_WRITE_2_ORBA		(6 << JR_REG_JRINT_ERR_TYPE_SHIFT)
#define JRINT_ERR_RES_B4_HALT		(7 << JR_REG_JRINT_ERR_TYPE_SHIFT)
#define JRINT_ERR_REM_TOO_MANY		(8 << JR_REG_JRINT_ERR_TYPE_SHIFT)
#define JRINT_ERR_ADD_TOO_MANY		(9 << JR_REG_JRINT_ERR_TYPE_SHIFT)
#define JRINT_ERR_HALT_MASK		0x0C
#define JRINT_ERR_HALT_INPROGRESS	0x04
#define JRINT_ERR_HALT_COMPLETE		0x08

#define JR_REG_JRCR_VAL_RESET		0x00000001

#define JR_REG_JRCFG_LO_ICTT_SHIFT	0x10
#define JR_REG_JRCFG_LO_ICDCT_SHIFT	0x08
#define JR_REG_JRCFG_LO_ICEN_EN		0x02

/*
 * Constants for Descriptor Processing errors
 */
#define SEC_HW_ERR_SSRC_NO_SRC		0x00
#define SEC_HW_ERR_SSRC_CCB_ERR		0x02
#define SEC_HW_ERR_SSRC_JMP_HALT_U	0x03
#define SEC_HW_ERR_SSRC_DECO		0x04
#define SEC_HW_ERR_SSRC_JR		0x06
#define SEC_HW_ERR_SSRC_JMP_HALT_COND   0x07

#define SEC_HW_ERR_DECO_HFN_THRESHOLD   0xF1
#define SEC_HW_ERR_CCB_ICV_CHECK_FAIL   0x0A

/*
 * Constants for descriptors
 */
/* Return higher 32 bits of physical address */
#define PHYS_ADDR_HI(phys_addr) \
	    (uint32_t)(((uint64_t)phys_addr) >> 32)

/* Return lower 32 bits of physical address */
#define PHYS_ADDR_LO(phys_addr) \
	    (uint32_t)(((uint64_t)phys_addr) & 0xFFFFFFFF)

/*
 * Macros for extracting error codes for the job ring
 */
#define JR_REG_JRINT_ERR_TYPE_EXTRACT(value)      ((value) & 0x00000F00)
#define JR_REG_JRINT_ERR_ORWI_EXTRACT(value)     \
	(((value) & 0x3FFF0000) >> JR_REG_JRINT_ERR_ORWI_SHIFT)
#define JR_REG_JRINT_JRE_EXTRACT(value)	   ((value) & JRINT_JRE)

/*
 * Macros for managing the job ring
 */
/* Read pointer to job ring input ring start address */
#if defined(RTE_ARCH_ARM64)
#define hw_get_inp_queue_base(jr) ((((dma_addr_t)GET_JR_REG(IRBA, \
				      (jr))) << 32) | \
				      (GET_JR_REG_LO(IRBA, (jr))))

/* Read pointer to job ring output ring start address */
#define hw_get_out_queue_base(jr) (((dma_addr_t)(GET_JR_REG(ORBA, \
				     (jr))) << 32) | \
				     (GET_JR_REG_LO(ORBA, (jr))))
#else
#define hw_get_inp_queue_base(jr)   ((dma_addr_t)(GET_JR_REG_LO(IRBA, (jr))))

#define hw_get_out_queue_base(jr)   ((dma_addr_t)(GET_JR_REG_LO(ORBA, (jr))))
#endif

/*
 * IRJA - Input Ring Jobs Added Register shows
 * how many new jobs were added to the Input Ring.
 */
#define hw_enqueue_desc_on_job_ring(job_ring) SET_JR_REG(IRJA, (job_ring), 1)

#define hw_set_input_ring_size(job_ring, size) SET_JR_REG(IRSR, job_ring, \
							 (size))

#define hw_set_output_ring_size(job_ring, size) SET_JR_REG(ORSR, job_ring, \
							  (size))

#if defined(RTE_ARCH_ARM64)
#define hw_set_input_ring_start_addr(job_ring, start_addr)	\
{								\
	SET_JR_REG(IRBA, job_ring, PHYS_ADDR_HI(start_addr));	\
	SET_JR_REG_LO(IRBA, job_ring, PHYS_ADDR_LO(start_addr));\
}

#define hw_set_output_ring_start_addr(job_ring, start_addr) \
{								\
	SET_JR_REG(ORBA, job_ring, PHYS_ADDR_HI(start_addr));	\
	SET_JR_REG_LO(ORBA, job_ring, PHYS_ADDR_LO(start_addr));\
}

#else
#define hw_set_input_ring_start_addr(job_ring, start_addr)	\
{								\
	SET_JR_REG(IRBA, job_ring, 0);	\
	SET_JR_REG_LO(IRBA, job_ring, PHYS_ADDR_LO(start_addr));\
}

#define hw_set_output_ring_start_addr(job_ring, start_addr) \
{								\
	SET_JR_REG(ORBA, job_ring, 0);	\
	SET_JR_REG_LO(ORBA, job_ring, PHYS_ADDR_LO(start_addr));\
}
#endif

/* ORJR - Output Ring Jobs Removed Register shows how many jobs were
 * removed from the Output Ring for processing by software. This is done after
 * the software has processed the entries.
 */
#define hw_remove_entries(jr, no_entries) SET_JR_REG(ORJR, (jr), (no_entries))

/* IRSA - Input Ring Slots Available register holds the number of entries in
 * the Job Ring's input ring. Once a job is enqueued, the value returned is
 * decremented by the hardware by the number of jobs enqueued.
 */
#define hw_get_available_slots(jr)		GET_JR_REG(IRSA, jr)

/* ORSFR - Output Ring Slots Full register holds the number of jobs which were
 * processed by the SEC and can be retrieved by the software. Once a job has
 * been processed by software, the user will call hw_remove_one_entry in order
 * to notify the SEC that the entry was processed.
 */
#define hw_get_no_finished_jobs(jr)		GET_JR_REG(ORSFR, jr)

/*
 * Macros for manipulating JR registers
 */
#if CORE_BYTE_ORDER == CAAM_BYTE_ORDER
#define sec_read_32(addr)	(*(volatile unsigned int *)(addr))
#define sec_write_32(addr, val)	(*(volatile unsigned int *)(addr) = (val))

#else
#define sec_read_32(addr)	rte_bswap32((*(volatile unsigned int *)(addr)))
#define sec_write_32(addr, val) \
			(*(volatile unsigned int *)(addr) = rte_bswap32(val))
#endif

#if CAAM_BYTE_ORDER == __LITTLE_ENDIAN
#define sec_read_64(addr)	(((u64)sec_read_32((u32 *)(addr) + 1) << 32) | \
				(sec_read_32((u32 *)(addr))))

#define sec_write_64(addr, val) {				\
	sec_write_32((u32 *)(addr) + 1, (u32)((val) >> 32));	\
	sec_write_32((u32 *)(addr), (u32)(val));		\
}
#else /* CAAM_BYTE_ORDER == __BIG_ENDIAN */
#define sec_read_64(addr)	(((u64)sec_read_32((u32 *)(addr)) << 32) | \
				(sec_read_32((u32 *)(addr) + 1)))

#define sec_write_64(addr, val) {				\
	sec_write_32((u32 *)(addr), (u32)((val) >> 32));	\
	sec_write_32((u32 *)(addr) + 1, (u32)(val));		\
}
#endif

#if defined(RTE_ARCH_ARM64)
#define sec_read_addr(a)	sec_read_64((a))
#define sec_write_addr(a, v)	sec_write_64((a), (v))
#else
#define sec_read_addr(a)	sec_read_32((a))
#define sec_write_addr(a, v)	sec_write_32((a), (v))
#endif

#define JR_REG(name, jr)	(CHAN_BASE(jr) + JR_REG_##name##_OFFSET)
#define JR_REG_LO(name, jr)	(CHAN_BASE(jr) + JR_REG_##name##_OFFSET_LO)

#define GET_JR_REG(name, jr)	(sec_read_32(JR_REG(name, (jr))))
#define GET_JR_REG_LO(name, jr)	(sec_read_32(JR_REG_LO(name, (jr))))

#define SET_JR_REG(name, jr, value) \
				(sec_write_32(JR_REG(name, (jr)), value))
#define SET_JR_REG_LO(name, jr, value) \
				(sec_write_32(JR_REG_LO(name, (jr)), value))

/* Lists the possible states for a job ring. */
typedef enum sec_job_ring_state_e {
	SEC_JOB_RING_STATE_STARTED,	/* Job ring is initialized */
	SEC_JOB_RING_STATE_RESET,	/* Job ring reset is in progress */
} sec_job_ring_state_t;

/* code or cmd block to caam */
struct sec_cdb {
	struct {
		union {
			uint32_t word;
			struct {
#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
				uint16_t rsvd63_48;
				unsigned int rsvd47_39:9;
				unsigned int idlen:7;
#else
				unsigned int idlen:7;
				unsigned int rsvd47_39:9;
				uint16_t rsvd63_48;
#endif
			} field;
		} __rte_packed hi;

		union {
			uint32_t word;
			struct {
#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
				unsigned int rsvd31_30:2;
				unsigned int fsgt:1;
				unsigned int lng:1;
				unsigned int offset:2;
				unsigned int abs:1;
				unsigned int add_buf:1;
				uint8_t pool_id;
				uint16_t pool_buffer_size;
#else
				uint16_t pool_buffer_size;
				uint8_t pool_id;
				unsigned int add_buf:1;
				unsigned int abs:1;
				unsigned int offset:2;
				unsigned int lng:1;
				unsigned int fsgt:1;
				unsigned int rsvd31_30:2;
#endif
			} field;
		} __rte_packed lo;
	} __rte_packed sh_hdr;

	uint32_t sh_desc[SEC_JOB_DESCRIPTOR_SIZE];
};

struct caam_jr_qp {
	struct sec_job_ring_t *ring;
	uint64_t rx_pkts;
	uint64_t rx_errs;
	uint64_t rx_poll_err;
	uint64_t tx_pkts;
	uint64_t tx_errs;
	uint64_t tx_ring_full;
};

struct sec_job_ring_t {
	/* TODO: Add wrapper macro to make it obvious this is the consumer index
	 * on the output ring
	 */
	uint32_t cidx;		/* Consumer index for job ring (jobs array).
				 * @note: cidx and pidx are accessed from
				 * different threads. Place the cidx and pidx
				 * inside the structure so that they lay on
				 * different cachelines, to avoid false sharing
				 * between threads when the threads run on
				 * different cores!
				 */
	/* TODO: Add wrapper macro to make it obvious this is the producer index
	 * on the input ring
	 */
	uint32_t pidx;		/* Producer index for job ring (jobs array) */

	phys_addr_t *input_ring;/* Ring of output descriptors received from SEC.
				 * Size of array is power of 2 to allow fast
				 * update of producer/consumer indexes with
				 * bitwise operations.
				 */

	struct sec_outring_entry *output_ring;
				/* Ring of output descriptors received from SEC.
				 * Size of array is power of 2 to allow fast
				 * update of producer/consumer indexes with
				 * bitwise operations.
				 */

	int irq_fd;		/* The file descriptor used for polling from
				 * user space for interrupts notifications
				 */
	uint32_t jr_mode;	/* Model used by SEC Driver to receive
				 * notifications from SEC.  Can be either
				 * of the three: #SEC_NOTIFICATION_TYPE_NAPI
				 * #SEC_NOTIFICATION_TYPE_IRQ or
				 * #SEC_NOTIFICATION_TYPE_POLL
				 */
	uint32_t napi_mode;	/* Job ring mode if NAPI mode is chosen
				 * Used only when jr_mode is set to
				 * #SEC_NOTIFICATION_TYPE_NAPI
				 */
	void *register_base_addr;	/* Base address for SEC's
					 * register memory for this job ring.
					 */
	uint8_t coalescing_en;		/* notifies if coelescing is
					 * enabled for the job ring
					 */
	sec_job_ring_state_t jr_state;	/* The state of this job ring */

	struct rte_mempool *ctx_pool;   /* per dev mempool for caam_jr_op_ctx */
	unsigned int max_nb_queue_pairs;
	unsigned int max_nb_sessions;
	struct caam_jr_qp qps[RTE_CAAM_MAX_NB_SEC_QPS]; /* i/o queue for sec */
};

/* Union describing the possible error codes that
 * can be set in the descriptor status word
 */
union hw_error_code {
	uint32_t error;
	union {
		struct {
			uint32_t ssrc:4;
			uint32_t ssed_val:28;
		} __rte_packed value;
		struct {
			uint32_t ssrc:4;
			uint32_t res:28;
		} __rte_packed no_status_src;
		struct {
			uint32_t ssrc:4;
			uint32_t jmp:1;
			uint32_t res:11;
			uint32_t desc_idx:8;
			uint32_t cha_id:4;
			uint32_t err_id:4;
		} __rte_packed ccb_status_src;
		struct {
			uint32_t ssrc:4;
			uint32_t jmp:1;
			uint32_t res:11;
			uint32_t desc_idx:8;
			uint32_t offset:8;
		} __rte_packed jmp_halt_user_src;
		struct {
			uint32_t ssrc:4;
			uint32_t jmp:1;
			uint32_t res:11;
			uint32_t desc_idx:8;
			uint32_t desc_err:8;
		} __rte_packed deco_src;
		struct {
			uint32_t ssrc:4;
			uint32_t res:17;
			uint32_t naddr:3;
			uint32_t desc_err:8;
		} __rte_packed jr_src;
		struct {
			uint32_t ssrc:4;
			uint32_t jmp:1;
			uint32_t res:11;
			uint32_t desc_idx:8;
			uint32_t cond:8;
		} __rte_packed jmp_halt_cond_src;
	} __rte_packed error_desc;
} __rte_packed;

/* @brief Initialize a job ring/channel in SEC device.
 * Write configuration register/s to properly initialize a job ring.
 *
 * @param [in] job_ring     The job ring
 *
 * @retval 0 for success
 * @retval other for error
 */
int hw_reset_job_ring(struct sec_job_ring_t *job_ring);

/* @brief Reset a job ring/channel in SEC device.
 * Write configuration register/s to reset a job ring.
 *
 * @param [in] job_ring     The job ring
 *
 * @retval 0 for success
 * @retval -1 in case job ring reset failed
 */
int hw_shutdown_job_ring(struct sec_job_ring_t *job_ring);

/* @brief Handle a job ring/channel error in SEC device.
 * Identify the error type and clear error bits if required.
 *
 * @param [in]  job_ring	The job ring
 * @param [in]  sec_error_code  The job ring's error code
 */
void hw_handle_job_ring_error(struct sec_job_ring_t *job_ring,
			      uint32_t sec_error_code);

/* @brief Handle a job ring error in the device.
 * Identify the error type and printout a explanatory
 * messages.
 *
 * @param [in]  job_ring	The job ring
 *
 */
void hw_job_ring_error_print(struct sec_job_ring_t *job_ring, int code);

/* @brief Set interrupt coalescing parameters on the Job Ring.
 * @param [in]  job_ring		The job ring
 * @param [in]  irq_coalesing_timer     Interrupt coalescing timer threshold.
 *					This value determines the maximum
 *					amount of time after processing a
 *					descriptor before raising an interrupt.
 * @param [in]  irq_coalescing_count    Interrupt coalescing descriptor count
 *					threshold.
 */
int hw_job_ring_set_coalescing_param(struct sec_job_ring_t *job_ring,
				     uint16_t irq_coalescing_timer,
				     uint8_t irq_coalescing_count);

/* @brief Enable interrupt coalescing on a job ring
 * @param [in]  job_ring		The job ring
 */
int hw_job_ring_enable_coalescing(struct sec_job_ring_t *job_ring);

/* @brief Disable interrupt coalescing on a job ring
 * @param [in]  job_ring		The job ring
 */
int hw_job_ring_disable_coalescing(struct sec_job_ring_t *job_ring);

#endif /* CAAM_JR_HW_SPECIFIC_H */
