/*   SPDX-License-Identifier: BSD-3-Clause
 *   Copyright(c) 2018 Advanced Micro Devices, Inc. All rights reserved.
 */

#ifndef _CCP_DEV_H_
#define _CCP_DEV_H_

#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#include <bus_pci_driver.h>
#include <rte_atomic.h>
#include <rte_byteorder.h>
#include <rte_io.h>
#include <rte_pci.h>
#include <rte_spinlock.h>
#include <rte_crypto_sym.h>
#include <cryptodev_pmd.h>

/* CCP PCI device identifiers */
#define AMD_PCI_VENDOR_ID 0x1022
#define AMD_PCI_CCP_5A 0x1456
#define AMD_PCI_CCP_5B 0x1468
#define AMD_PCI_CCP_RV 0x15df

/**< CCP specific */
#define MAX_HW_QUEUES                   5
#define CCP_MAX_TRNG_RETRIES		10
#define CCP_ALIGN(x, y) ((((x) + (y - 1)) / y) * y)

/**< CCP Register Mappings */
#define Q_MASK_REG                      0x000
#define TRNG_OUT_REG                    0x00c

/* CCP Version 5 Specifics */
#define CMD_QUEUE_MASK_OFFSET		0x00
#define	CMD_QUEUE_PRIO_OFFSET		0x04
#define CMD_REQID_CONFIG_OFFSET		0x08
#define	CMD_CMD_TIMEOUT_OFFSET		0x10
#define LSB_PUBLIC_MASK_LO_OFFSET	0x18
#define LSB_PUBLIC_MASK_HI_OFFSET	0x1C
#define LSB_PRIVATE_MASK_LO_OFFSET	0x20
#define LSB_PRIVATE_MASK_HI_OFFSET	0x24

#define CMD_Q_CONTROL_BASE		0x0000
#define CMD_Q_TAIL_LO_BASE		0x0004
#define CMD_Q_HEAD_LO_BASE		0x0008
#define CMD_Q_INT_ENABLE_BASE		0x000C
#define CMD_Q_INTERRUPT_STATUS_BASE	0x0010

#define CMD_Q_STATUS_BASE		0x0100
#define CMD_Q_INT_STATUS_BASE		0x0104

#define	CMD_CONFIG_0_OFFSET		0x6000
#define	CMD_TRNG_CTL_OFFSET		0x6008
#define	CMD_AES_MASK_OFFSET		0x6010
#define	CMD_CLK_GATE_CTL_OFFSET		0x603C

/* Address offset between two virtual queue registers */
#define CMD_Q_STATUS_INCR		0x1000

/* Bit masks */
#define CMD_Q_RUN			0x1
#define CMD_Q_SIZE			0x1F
#define CMD_Q_SHIFT			3
#define COMMANDS_PER_QUEUE		8192

#define QUEUE_SIZE_VAL                  ((ffs(COMMANDS_PER_QUEUE) - 2) & \
					 CMD_Q_SIZE)
#define Q_DESC_SIZE                     sizeof(struct ccp_desc)
#define Q_SIZE(n)                       (COMMANDS_PER_QUEUE*(n))

#define INT_COMPLETION                  0x1
#define INT_ERROR                       0x2
#define INT_QUEUE_STOPPED               0x4
#define ALL_INTERRUPTS                  (INT_COMPLETION| \
					 INT_ERROR| \
					 INT_QUEUE_STOPPED)

#define LSB_REGION_WIDTH                5
#define MAX_LSB_CNT                     8

#define LSB_SIZE                        16
#define LSB_ITEM_SIZE                   32
#define SLSB_MAP_SIZE                   (MAX_LSB_CNT * LSB_SIZE)
#define LSB_ENTRY_NUMBER(LSB_ADDR)      (LSB_ADDR / LSB_ITEM_SIZE)

/* General CCP Defines */

#define CCP_SB_BYTES                    32
/* Word 0 */
#define CCP_CMD_DW0(p)		((p)->dw0)
#define CCP_CMD_SOC(p)		(CCP_CMD_DW0(p).soc)
#define CCP_CMD_IOC(p)		(CCP_CMD_DW0(p).ioc)
#define CCP_CMD_INIT(p)	        (CCP_CMD_DW0(p).init)
#define CCP_CMD_EOM(p)		(CCP_CMD_DW0(p).eom)
#define CCP_CMD_FUNCTION(p)	(CCP_CMD_DW0(p).function)
#define CCP_CMD_ENGINE(p)	(CCP_CMD_DW0(p).engine)
#define CCP_CMD_PROT(p)	        (CCP_CMD_DW0(p).prot)

/* Word 1 */
#define CCP_CMD_DW1(p)		((p)->length)
#define CCP_CMD_LEN(p)		(CCP_CMD_DW1(p))

/* Word 2 */
#define CCP_CMD_DW2(p)		((p)->src_lo)
#define CCP_CMD_SRC_LO(p)	(CCP_CMD_DW2(p))

/* Word 3 */
#define CCP_CMD_DW3(p)		((p)->dw3)
#define CCP_CMD_SRC_MEM(p)	((p)->dw3.src_mem)
#define CCP_CMD_SRC_HI(p)	((p)->dw3.src_hi)
#define CCP_CMD_LSB_ID(p)	((p)->dw3.lsb_cxt_id)
#define CCP_CMD_FIX_SRC(p)	((p)->dw3.fixed)

/* Words 4/5 */
#define CCP_CMD_DW4(p)		((p)->dw4)
#define CCP_CMD_DST_LO(p)	(CCP_CMD_DW4(p).dst_lo)
#define CCP_CMD_DW5(p)		((p)->dw5.fields.dst_hi)
#define CCP_CMD_DST_HI(p)	(CCP_CMD_DW5(p))
#define CCP_CMD_DST_MEM(p)	((p)->dw5.fields.dst_mem)
#define CCP_CMD_FIX_DST(p)	((p)->dw5.fields.fixed)
#define CCP_CMD_SHA_LO(p)	((p)->dw4.sha_len_lo)
#define CCP_CMD_SHA_HI(p)	((p)->dw5.sha_len_hi)

/* Word 6/7 */
#define CCP_CMD_DW6(p)		((p)->key_lo)
#define CCP_CMD_KEY_LO(p)	(CCP_CMD_DW6(p))
#define CCP_CMD_DW7(p)		((p)->dw7)
#define CCP_CMD_KEY_HI(p)	((p)->dw7.key_hi)
#define CCP_CMD_KEY_MEM(p)	((p)->dw7.key_mem)

/* bitmap */
enum {
	BITS_PER_WORD = sizeof(unsigned long) * CHAR_BIT
};

#define WORD_OFFSET(b) ((b) / BITS_PER_WORD)
#define BIT_OFFSET(b)  ((b) % BITS_PER_WORD)

#define CCP_DIV_ROUND_UP(n, d)  (((n) + (d) - 1) / (d))
#define CCP_BITMAP_SIZE(nr) \
	CCP_DIV_ROUND_UP(nr, CHAR_BIT * sizeof(unsigned long))

#define CCP_BITMAP_FIRST_WORD_MASK(start) \
	(~0UL << ((start) & (BITS_PER_WORD - 1)))
#define CCP_BITMAP_LAST_WORD_MASK(nbits) \
	(~0UL >> (-(nbits) & (BITS_PER_WORD - 1)))

#define __ccp_round_mask(x, y) ((typeof(x))((y)-1))
#define ccp_round_down(x, y) ((x) & ~__ccp_round_mask(x, y))

/** CCP registers Write/Read */

static inline void ccp_pci_reg_write(void *base, int offset,
				     uint32_t value)
{
	volatile void *reg_addr = ((uint8_t *)base + offset);

	rte_write32((rte_cpu_to_le_32(value)), reg_addr);
}

static inline uint32_t ccp_pci_reg_read(void *base, int offset)
{
	volatile void *reg_addr = ((uint8_t *)base + offset);

	return rte_le_to_cpu_32(rte_read32(reg_addr));
}

#define CCP_READ_REG(hw_addr, reg_offset) \
	ccp_pci_reg_read(hw_addr, reg_offset)

#define CCP_WRITE_REG(hw_addr, reg_offset, value) \
	ccp_pci_reg_write(hw_addr, reg_offset, value)

/**
 * A structure describing a CCP command queue.
 */
struct __rte_cache_aligned ccp_queue {
	struct ccp_device *dev;
	char memz_name[RTE_MEMZONE_NAMESIZE];

	rte_atomic64_t free_slots;
	/**< available free slots updated from enq/deq calls */

	/* Queue identifier */
	uint64_t id;	/**< queue id */
	uint64_t qidx;	/**< queue index */
	uint64_t qsize;	/**< queue size */

	/* Queue address */
	struct ccp_desc *qbase_desc;
	void *qbase_addr;
	phys_addr_t qbase_phys_addr;
	/**< queue-page registers addr */
	void *reg_base;

	uint32_t qcontrol;
	/**< queue ctrl reg */

	int lsb;
	/**< lsb region assigned to queue */
	unsigned long lsbmask;
	/**< lsb regions queue can access */
	unsigned long lsbmap[CCP_BITMAP_SIZE(LSB_SIZE)];
	/**< all lsb resources which queue is using */
	uint32_t sb_key;
	/**< lsb assigned for queue */
	uint32_t sb_iv;
	/**< lsb assigned for iv */
	uint32_t sb_sha;
	/**< lsb assigned for sha ctx */
	uint32_t sb_hmac;
	/**< lsb assigned for hmac ctx */
};

/**
 * A structure describing a CCP device.
 */
struct __rte_cache_aligned ccp_device {
	TAILQ_ENTRY(ccp_device) next;
	int id;
	/**< ccp dev id on platform */
	struct ccp_queue cmd_q[MAX_HW_QUEUES];
	/**< ccp queue */
	int cmd_q_count;
	/**< no. of ccp Queues */
	struct rte_pci_device *pci;
	/**< ccp pci device */
	unsigned long lsbmap[CCP_BITMAP_SIZE(SLSB_MAP_SIZE)];
	/**< shared lsb mask of ccp */
	rte_spinlock_t lsb_lock;
	/**< protection for shared lsb region allocation */
	int qidx;
	/**< current queue index */
	int hwrng_retries;
	/**< retry counter for CCP TRNG */
};

/**< CCP H/W engine related */
/**
 * ccp_engine - CCP operation identifiers
 *
 * @CCP_ENGINE_AES: AES operation
 * @CCP_ENGINE_XTS_AES: 128-bit XTS AES operation
 * @CCP_ENGINE_3DES: DES/3DES operation
 * @CCP_ENGINE_SHA: SHA operation
 * @CCP_ENGINE_RSA: RSA operation
 * @CCP_ENGINE_PASSTHRU: pass-through operation
 * @CCP_ENGINE_ZLIB_DECOMPRESS: unused
 * @CCP_ENGINE_ECC: ECC operation
 */
enum ccp_engine {
	CCP_ENGINE_AES = 0,
	CCP_ENGINE_XTS_AES_128,
	CCP_ENGINE_3DES,
	CCP_ENGINE_SHA,
	CCP_ENGINE_RSA,
	CCP_ENGINE_PASSTHRU,
	CCP_ENGINE_ZLIB_DECOMPRESS,
	CCP_ENGINE_ECC,
	CCP_ENGINE__LAST,
};

/* Passthru engine */
/**
 * ccp_passthru_bitwise - type of bitwise passthru operation
 *
 * @CCP_PASSTHRU_BITWISE_NOOP: no bitwise operation performed
 * @CCP_PASSTHRU_BITWISE_AND: perform bitwise AND of src with mask
 * @CCP_PASSTHRU_BITWISE_OR: perform bitwise OR of src with mask
 * @CCP_PASSTHRU_BITWISE_XOR: perform bitwise XOR of src with mask
 * @CCP_PASSTHRU_BITWISE_MASK: overwrite with mask
 */
enum ccp_passthru_bitwise {
	CCP_PASSTHRU_BITWISE_NOOP = 0,
	CCP_PASSTHRU_BITWISE_AND,
	CCP_PASSTHRU_BITWISE_OR,
	CCP_PASSTHRU_BITWISE_XOR,
	CCP_PASSTHRU_BITWISE_MASK,
	CCP_PASSTHRU_BITWISE__LAST,
};

/**
 * ccp_passthru_byteswap - type of byteswap passthru operation
 *
 * @CCP_PASSTHRU_BYTESWAP_NOOP: no byte swapping performed
 * @CCP_PASSTHRU_BYTESWAP_32BIT: swap bytes within 32-bit words
 * @CCP_PASSTHRU_BYTESWAP_256BIT: swap bytes within 256-bit words
 */
enum ccp_passthru_byteswap {
	CCP_PASSTHRU_BYTESWAP_NOOP = 0,
	CCP_PASSTHRU_BYTESWAP_32BIT,
	CCP_PASSTHRU_BYTESWAP_256BIT,
	CCP_PASSTHRU_BYTESWAP__LAST,
};

/**
 * CCP passthru
 */
struct ccp_passthru {
	phys_addr_t src_addr;
	phys_addr_t dest_addr;
	enum ccp_passthru_bitwise bit_mod;
	enum ccp_passthru_byteswap byte_swap;
	int len;
	int dir;
};

/* CCP version 5: Union to define the function field (cmd_reg1/dword0) */
union ccp_function {
	struct {
		uint16_t size:7;
		uint16_t encrypt:1;
		uint16_t mode:5;
		uint16_t type:2;
	} aes;
	struct {
		uint16_t size:7;
		uint16_t encrypt:1;
		uint16_t mode:5;
		uint16_t type:2;
	} des;
	struct {
		uint16_t size:7;
		uint16_t encrypt:1;
		uint16_t rsvd:5;
		uint16_t type:2;
	} aes_xts;
	struct {
		uint16_t rsvd1:10;
		uint16_t type:4;
		uint16_t rsvd2:1;
	} sha;
	struct {
		uint16_t mode:3;
		uint16_t size:12;
	} rsa;
	struct {
		uint16_t byteswap:2;
		uint16_t bitwise:3;
		uint16_t reflect:2;
		uint16_t rsvd:8;
	} pt;
	struct  {
		uint16_t rsvd:13;
	} zlib;
	struct {
		uint16_t size:10;
		uint16_t type:2;
		uint16_t mode:3;
	} ecc;
	uint16_t raw;
};


/**
 * descriptor for version 5 CPP commands
 * 8 32-bit words:
 * word 0: function; engine; control bits
 * word 1: length of source data
 * word 2: low 32 bits of source pointer
 * word 3: upper 16 bits of source pointer; source memory type
 * word 4: low 32 bits of destination pointer
 * word 5: upper 16 bits of destination pointer; destination memory
 * type
 * word 6: low 32 bits of key pointer
 * word 7: upper 16 bits of key pointer; key memory type
 */
struct dword0 {
	uint32_t soc:1;
	uint32_t ioc:1;
	uint32_t rsvd1:1;
	uint32_t init:1;
	uint32_t eom:1;
	uint32_t function:15;
	uint32_t engine:4;
	uint32_t prot:1;
	uint32_t rsvd2:7;
};

struct dword3 {
	uint32_t src_hi:16;
	uint32_t src_mem:2;
	uint32_t lsb_cxt_id:8;
	uint32_t rsvd1:5;
	uint32_t fixed:1;
};

union dword4 {
	uint32_t dst_lo;	/* NON-SHA */
	uint32_t sha_len_lo;	/* SHA */
};

union dword5 {
	struct {
		uint32_t dst_hi:16;
		uint32_t dst_mem:2;
		uint32_t rsvd1:13;
		uint32_t fixed:1;
	}
	fields;
	uint32_t sha_len_hi;
};

struct dword7 {
	uint32_t key_hi:16;
	uint32_t key_mem:2;
	uint32_t rsvd1:14;
};

struct ccp_desc {
	struct dword0 dw0;
	uint32_t length;
	uint32_t src_lo;
	struct dword3 dw3;
	union dword4 dw4;
	union dword5 dw5;
	uint32_t key_lo;
	struct dword7 dw7;
};

/**
 * ccp memory type
 */
enum ccp_memtype {
	CCP_MEMTYPE_SYSTEM = 0,
	CCP_MEMTYPE_SB,
	CCP_MEMTYPE_LOCAL,
	CCP_MEMTYPE_LAST,
};

/**
 * cmd id to follow order
 */
enum ccp_cmd_order {
	CCP_CMD_CIPHER = 0,
	CCP_CMD_AUTH,
	CCP_CMD_CIPHER_HASH,
	CCP_CMD_HASH_CIPHER,
	CCP_CMD_COMBINED,
	CCP_CMD_NOT_SUPPORTED,
};

static inline uint32_t
low32_value(unsigned long addr)
{
	return ((uint64_t)addr) & 0x0ffffffff;
}

static inline uint32_t
high32_value(unsigned long addr)
{
	return ((uint64_t)addr >> 32) & 0x00000ffff;
}

/*
 * Start CCP device
 */
int ccp_dev_start(struct rte_cryptodev *dev);

/**
 * Initialize one ccp device
 *
 * @dev rte pci device
 * @return 0 on success otherwise -1
 */
int ccp_probe_device(struct rte_pci_device *pci_dev);

/**
 * allocate a ccp command queue
 *
 * @dev rte crypto device
 * @param slot_req number of required
 * @return allotted CCP queue on success otherwise NULL
 */
struct ccp_queue *ccp_allot_queue(struct rte_cryptodev *dev, int slot_req);

/**
 * read hwrng value
 *
 * @param trng_value data pointer to write RNG value
 * @return 0 on success otherwise -1
 */
int ccp_read_hwrng(uint32_t *trng_value);

#endif /* _CCP_DEV_H_ */
