/* ==========================================================================
 * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
 * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
 * otherwise expressly agreed to in writing between Synopsys and you.
 *
 * The Software IS NOT an item of Licensed Software or Licensed Product under
 * any End User Software License Agreement or Agreement for Licensed Product
 * with Synopsys or any supplement thereto. You are permitted to use and
 * redistribute this Software in source and binary forms, with or without
 * modification, provided that redistributions of source code must retain this
 * notice. You may not view, use, disclose, copy or distribute this file or
 * any information contained herein except pursuant to this license grant from
 * Synopsys. If you do not agree with this notice, including the disclaimer
 * below, then you are not authorized to use the Software.
 *
 * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * ========================================================================== */

#if !defined(__DWC_OTG_CFI_H__)
#define __DWC_OTG_CFI_H__

#include "dwc_otg_pcd.h"
#include "dwc_cfi_common.h"

/**
 * @file
 * This file contains the CFI related OTG PCD specific common constants,
 * interfaces(functions and macros) and data structures.The CFI Protocol is an
 * optional interface for internal testing purposes that a DUT may implement to
 * support testing of configurable features.
 *
 */

struct dwc_otg_pcd;
struct dwc_otg_pcd_ep;

/** OTG CFI Features (properties) ID constants */
/** This is a request for all Core Features */
#define FT_ID_DMA_MODE					0x0001
#define FT_ID_DMA_BUFFER_SETUP			0x0002
#define FT_ID_DMA_BUFF_ALIGN			0x0003
#define FT_ID_DMA_CONCAT_SETUP			0x0004
#define FT_ID_DMA_CIRCULAR				0x0005
#define FT_ID_THRESHOLD_SETUP			0x0006
#define FT_ID_DFIFO_DEPTH				0x0007
#define FT_ID_TX_FIFO_DEPTH				0x0008
#define FT_ID_RX_FIFO_DEPTH				0x0009

/**********************************************************/
#define CFI_INFO_DEF

#ifdef CFI_INFO_DEF
#define CFI_INFO(fmt...)	DWC_PRINTF("CFI: " fmt);
#else
#define CFI_INFO(fmt...)
#endif

#define min(x, y) ({ \
	x < y ? x : y; })

#define max(x, y) ({ \
	x > y ? x : y; })

/**
 * Descriptor DMA SG Buffer setup structure (SG buffer). This structure is
 * also used for setting up a buffer for Circular DDMA.
 */
struct _ddma_sg_buffer_setup {
#define BS_SG_VAL_DESC_LEN	6
	/* The OUT EP address */
	uint8_t bOutEndpointAddress;
	/* The IN EP address */
	uint8_t bInEndpointAddress;
	/* Number of bytes to put between transfer segments (must be DWORD boundaries) */
	uint8_t bOffset;
	/* The number of transfer segments (a DMA descriptors per each segment) */
	uint8_t bCount;
	/* Size (in byte) of each transfer segment */
	uint16_t wSize;
} __attribute__ ((packed));
typedef struct _ddma_sg_buffer_setup ddma_sg_buffer_setup_t;

/** Descriptor DMA Concatenation Buffer setup structure */
struct _ddma_concat_buffer_setup_hdr {
#define BS_CONCAT_VAL_HDR_LEN	4
	/* The endpoint for which the buffer is to be set up */
	uint8_t bEndpointAddress;
	/* The count of descriptors to be used */
	uint8_t bDescCount;
	/* The total size of the transfer */
	uint16_t wSize;
} __attribute__ ((packed));
typedef struct _ddma_concat_buffer_setup_hdr ddma_concat_buffer_setup_hdr_t;

/** Descriptor DMA Concatenation Buffer setup structure */
struct _ddma_concat_buffer_setup {
	/* The SG header */
	ddma_concat_buffer_setup_hdr_t hdr;

	/* The XFER sizes pointer (allocated dynamically) */
	uint16_t *wTxBytes;
} __attribute__ ((packed));
typedef struct _ddma_concat_buffer_setup ddma_concat_buffer_setup_t;

/** Descriptor DMA Alignment Buffer setup structure */
struct _ddma_align_buffer_setup {
#define BS_ALIGN_VAL_HDR_LEN	2
	uint8_t bEndpointAddress;
	uint8_t bAlign;
} __attribute__ ((packed));
typedef struct _ddma_align_buffer_setup ddma_align_buffer_setup_t;

/** Transmit FIFO Size setup structure */
struct _tx_fifo_size_setup {
	uint8_t bEndpointAddress;
	uint16_t wDepth;
} __attribute__ ((packed));
typedef struct _tx_fifo_size_setup tx_fifo_size_setup_t;

/** Transmit FIFO Size setup structure */
struct _rx_fifo_size_setup {
	uint16_t wDepth;
} __attribute__ ((packed));
typedef struct _rx_fifo_size_setup rx_fifo_size_setup_t;

/**
 * struct cfi_usb_ctrlrequest - the CFI implementation of the struct usb_ctrlrequest
 * This structure encapsulates the standard usb_ctrlrequest and adds a pointer
 * to the data returned in the data stage of a 3-stage Control Write requests.
 */
struct cfi_usb_ctrlrequest {
	uint8_t bRequestType;
	uint8_t bRequest;
	uint16_t wValue;
	uint16_t wIndex;
	uint16_t wLength;
	uint8_t *data;
} UPACKED;

/*---------------------------------------------------------------------------*/

/**
 * The CFI wrapper of the enabled and activated dwc_otg_pcd_ep structures.
 * This structure is used to store the buffer setup data for any
 * enabled endpoint in the PCD.
 */
struct cfi_ep {
	/* Entry for the list container */
	dwc_list_link_t lh;
	/* Pointer to the active PCD endpoint structure */
	struct dwc_otg_pcd_ep *ep;
	/* The last descriptor in the chain of DMA descriptors of the endpoint */
	struct dwc_otg_dma_desc *dma_desc_last;
	/* The SG feature value */
	ddma_sg_buffer_setup_t *bm_sg;
	/* The Circular feature value */
	ddma_sg_buffer_setup_t *bm_circ;
	/* The Concatenation feature value */
	ddma_concat_buffer_setup_t *bm_concat;
	/* The Alignment feature value */
	ddma_align_buffer_setup_t *bm_align;
	/* XFER length */
	uint32_t xfer_len;
	/*
	 * Count of DMA descriptors currently used.
	 * The total should not exceed the MAX_DMA_DESCS_PER_EP value
	 * defined in the dwc_otg_cil.h
	 */
	uint32_t desc_count;
};
typedef struct cfi_ep cfi_ep_t;

typedef struct cfi_dma_buff {
#define CFI_IN_BUF_LEN	1024
#define CFI_OUT_BUF_LEN	1024
	dma_addr_t addr;
	uint8_t *buf;
} cfi_dma_buff_t;

struct cfiobject;

/**
 * This is the interface for the CFI operations.
 *
 * @param	ep_enable			Called when any endpoint is enabled and activated.
 * @param	release				Called when the CFI object is released and it needs to correctly
 *								deallocate the dynamic memory
 * @param	ctrl_write_complete	Called when the data stage of the request is complete
 */
typedef struct cfi_ops {
	int (*ep_enable) (struct cfiobject *cfi, struct dwc_otg_pcd *pcd,
			  struct dwc_otg_pcd_ep *ep);
	void *(*ep_alloc_buf) (struct cfiobject *cfi, struct dwc_otg_pcd *pcd,
			       struct dwc_otg_pcd_ep *ep, dma_addr_t *dma,
			       unsigned size, gfp_t flags);
	void (*release) (struct cfiobject *cfi);
	int (*ctrl_write_complete) (struct cfiobject *cfi,
				    struct dwc_otg_pcd *pcd);
	void (*build_descriptors) (struct cfiobject *cfi,
				   struct dwc_otg_pcd *pcd,
				   struct dwc_otg_pcd_ep *ep,
				   dwc_otg_pcd_request_t *req);
} cfi_ops_t;

struct cfiobject {
	cfi_ops_t ops;
	struct dwc_otg_pcd *pcd;
	struct usb_gadget *gadget;

	/* Buffers used to send/receive CFI-related request data */
	cfi_dma_buff_t buf_in;
	cfi_dma_buff_t buf_out;

	/* CFI specific Control request wrapper */
	struct cfi_usb_ctrlrequest ctrl_req;

	/* The list of active EP's in the PCD of type cfi_ep_t */
	dwc_list_link_t active_eps;

	/* This flag shall control the propagation of a specific request
	 * to the gadget's processing routines.
	 * 0 - no gadget handling
	 * 1 - the gadget needs to know about this request (w/o completing a status
	 * phase - just return a 0 to the _setup callback)
	 */
	uint8_t need_gadget_att;

	/* Flag indicating whether the status IN phase needs to be
	 * completed by the PCD
	 */
	uint8_t need_status_in_complete;
};
typedef struct cfiobject cfiobject_t;

#define DUMP_MSG

#if defined(DUMP_MSG)
static inline void dump_msg(const u8 *buf, unsigned int length)
{
	unsigned int start, num, i;
	char line[52], *p;

	if (length >= 512)
		return;

	start = 0;
	while (length > 0) {
		num = min(length, 16u);
		p = line;
		for (i = 0; i < num; ++i) {
			if (i == 8)
				*p++ = ' ';
			DWC_SPRINTF(p, " %02x", buf[i]);
			p += 3;
		}
		*p = 0;
		DWC_DEBUG("%6x: %s\n", start, line);
		buf += num;
		start += num;
		length -= num;
	}
}
#else
static inline void dump_msg(const u8 *buf, unsigned int length)
{
}
#endif

/**
 * This function returns a pointer to cfi_ep_t object with the addr address.
 */
static inline struct cfi_ep *get_cfi_ep_by_addr(struct cfiobject *cfi,
						uint8_t addr)
{
	struct cfi_ep *pcfiep;
	dwc_list_link_t *tmp;

	DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
		pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);

		if (pcfiep->ep->desc->bEndpointAddress == addr)
			return pcfiep;
	}

	return NULL;
}

/**
 * This function returns a pointer to cfi_ep_t object that matches
 * the dwc_otg_pcd_ep object.
 */
static inline struct cfi_ep *get_cfi_ep_by_pcd_ep(struct cfiobject *cfi,
						  struct dwc_otg_pcd_ep *ep)
{
	struct cfi_ep *pcfiep = NULL;
	dwc_list_link_t *tmp;

	DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
		pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
		if (pcfiep->ep == ep)
			return pcfiep;
	}
	return NULL;
}

int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl);

#endif /* (__DWC_OTG_CFI_H__) */
