/******************************************************************************
 *
 * Copyright (C) 2016-2017 Cadence Design Systems, Inc.
 * All rights reserved worldwide.
 *
 * Copyright 2017-2018 NXP
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 ******************************************************************************
 *
 * util.h
 *
 ******************************************************************************
 */

#ifndef UTIL_H_
#define UTIL_H_

#include "API_General.h"
#ifndef __UBOOT__
#include <stdarg.h>
#include <stdint.h>
#else
#include <common.h>
#endif
/**
 * \addtogroup UTILS
 * \{
 */
#define INTERNAL_CMD_HEAD_SIZE 4

/**
 * \brief expands to blocking function body
 * \param x - function call
 */
#define internal_block_function(x)		\
do {						\
	CDN_API_STATUS ret;			\
	do {					\
		ret = x;			\
	} while (ret == CDN_BSY || ret == CDN_STARTED); \
	return ret;				\
} while (0)

/**
 * \brief write message and write response (if any), non-blocking way.
 * Also sets state.running = 0
 */
#define INTERNAL_PROCESS_MESSAGES				\
do {								\
	if (state.txenable && !internal_mbox_tx_process().txend)	\
		return CDN_BSY;					\
	if (state.rxenable && !internal_mbox_rx_process().rxend)	\
		return CDN_BSY;					\
	state.running = 0;					\
} while (0)

#define internal_opcode_ok_or_return(module, opcode)			\
do {									\
	CDN_API_STATUS ret = internal_test_rx_head(module, opcode);	\
	if (ret != CDN_OK)						\
		return ret;						\
} while (0)

#define internal_opcode_match_or_return()			\
do {								\
	CDN_API_STATUS ret = internal_test_rx_head_match();	\
	if (ret != CDN_OK)	\
		return ret;					\
} while (0)

/*
 * macro for simple tx only command, command format as in mkfullmsg
 * (with count)
 */
#define internal_macro_command_tx(module, opcode, bustype, command...)	\
do {									\
	if (!state.running) {						\
		internal_tx_mkfullmsg(module, opcode, command);		\
		state.bus_type = bustype;				\
		return CDN_STARTED;					\
	}								\
	INTERNAL_PROCESS_MESSAGES;					\
} while (0)

/*
 * macro for command with response with matching opcode, command format as in
 * mkfullmsg (with count)
 */
#define internal_macro_command_txrx(module, opcode, bustype, command...) \
do {									\
	if (!state.running) {						\
		internal_tx_mkfullmsg(module, opcode, command);		\
		state.bus_type = bustype;				\
		state.rxenable = 1;					\
		return CDN_STARTED;					\
	}								\
	INTERNAL_PROCESS_MESSAGES;					\
	internal_opcode_match_or_return();				\
} while (0)

typedef struct {
	/** apb write status */
	enum tx_status_enum {
	/** one or more bytes written */
		CDN_TX_WRITE = 0,
	/** nothing to write */
		CDN_TX_NOTHING = 1,
	/** mailbox full, 0 bytes written */
		CDN_TX_FULL = 2,
	/** APB error while writing */
		CDN_TX_APB_ERROR = 3
	} tx_status:3;
	/** apb read status */
	enum rx_status_enum {
	/** 1 or more bytes read */
		CDN_RX_READ = 0,
	/** mailbox empty, 0 bytes read */
		CDN_RX_EMPTY = 1,
	/** apb error while reading */
		CDN_RX_APB_ERROR = 2
	} rx_status:2;
	/** indicates end of currenly recived message */
	unsigned char rxend:1;
	/** end of tx message reached */
	unsigned char txend:1;
} INTERNAL_MBOX_STATUS;

/**
 * \brief put val into dest in big endian format
 * \param val - value to put
 * \param dest - place to put value
 * \param bytes - true size of val in bytes. for example if
 *        bytes = 2 val is treated as short int
 */
void internal_itobe(int val, volatile unsigned char *dest, int bytes);

/**
 * \brief read big endian value from src and return it
 * \param src - source to read from
 * \param bytes - size of read value
 * \return result
 */
u32 internal_betoi(volatile u8 const *src, u8 bytes);

/**
 * \brief create message from size and value pairs; also sets
 *        state.running and state.txEnable
 * \param dest - pointer to write message to
 * \param valNo - number of values to write
 * \param ... - pairs of size and value, each value is written
 *        after another. if size is positive value, value is
 *        written with #internal_itobe, if size is negative,
 *        value is treated as src pointer for memcpy
 *
 * example:
 *
 *  unsigned short x = 0xAABB;
 *
 *  internal_mkmsg(dest, 3, 1, 1, 2, 3, -2, &x);
 *
 *  will write 01 00 03 AA BB to dest
 */
unsigned int internal_mkmsg(volatile unsigned char *dest, int valno, ...);
unsigned int internal_vmkmsg(volatile unsigned char *dest, int valno,
			     va_list vl);

/**
 * \brief setup message header in txBuffer, set txEnable = 1
 */
void internal_mbox_tx_enable(unsigned char module, unsigned char opcode,
			     unsigned short length);

/**
 * \brief write from txBuffer to mailbox until full or end of message.
 *
 * when txEnable == 0 writes nothing
 * when write reaches end of message set txEnable = 0
 */

/**
 * \brief combination of #internal_mkmsg and #internal_mbox_tx_enable
 *
 * #internal_mkmsg dest and #internal_mbox_tx_enable length are determined
 * automatically this function also sets state.txEnable = 1 and
 * state.running
 */
void internal_tx_mkfullmsg(unsigned char module, unsigned char opcode,
			   int valno, ...);
void internal_vtx_mkfullmsg(unsigned char module, unsigned char opcode,
			    int valno, va_list vl);

/**
 * \brief read from state.txBuffer and store results in specified pointers
 * \param valNo - numbero of values to read
 * \param ... - pairs of size and ptr
 *
 * this function is similar to #internal_mkmsg -
 *
 * when size is positive read value using #internal_betoi
 * when size is negative mempcy from txBuffer to ptr -size bytes
 * when size is 0 write to ptr address of current position in rxbuffer
 * when ptr is NULL ignore size bytes (if size is negative this
 * will rewind buffer)
 */
void internal_readmsg(int valno, ...);
void internal_vreadmsg(int valno, va_list vl);

INTERNAL_MBOX_STATUS internal_mbox_tx_process(void);
/**
 * \brief read to rxBuffer from mailbox until empty or end of message
 *
 * when rxEnable == 0 reads nothing
 * when end of message reached sets rxEnable = 0
 */
INTERNAL_MBOX_STATUS internal_mbox_rx_process(void);

/**
 * \brief check if apb is available
 * \return !(rxEnable && txEable)
 */
unsigned int internal_apb_available(void);

/**
 * \brief test if parameters match module and opcode in rxBuffer
 * \return CDN_OK or CDN_BAD_OPCODE or CDN_BAD_MODULE
 */
CDN_API_STATUS internal_test_rx_head(unsigned char module,
				     unsigned char opcode);

CDN_API_STATUS internal_test_rx_head_match(void);

/**
 * \brief print current fw and lib version
 */
void print_fw_ver(void);

typedef struct {
	unsigned char txbuffer[1024];
	unsigned char rxbuffer[1024];
	unsigned int txi;	/* iterators */
	unsigned int rxi;
	unsigned char txenable;	/*data readt to send*/
	unsigned char rxenable;
	unsigned char running;
	CDN_BUS_TYPE bus_type;
	unsigned int tmp;
} state_struct;

extern state_struct state;
extern int cdn_bus_read(unsigned int addr, unsigned int *value);
extern int cdn_bus_write(unsigned int addr, unsigned int value);
unsigned short internal_get_msg_len(void);

#endif
