/* Copyright 2015 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Hybrid DMA/Interrupt based USART RX driver for STM32
 */
#ifndef __CROS_EC_USART_RX_DMA_H
#define __CROS_EC_USART_RX_DMA_H

#include "producer.h"
#include "dma.h"
#include "queue.h"
#include "usart.h"

/*
 * Only reference the usart_rx_dma_info function if CONFIG_CMD_USART_INFO
 * is defined.  This allows the compiler to remove this function as dead code
 * when CONFIG_CMD_USART_INFO is not defined.
 */
#ifdef CONFIG_CMD_USART_INFO
#define USART_RX_DMA_INFO usart_rx_dma_info
#else
#define USART_RX_DMA_INFO NULL
#endif

/*
 * Construct a USART RX instance for DMA using the given DMA channel.
 *
 * This macro creates a new usart_rx_dma struct, complete with in RAM state,
 * the contained usart_rx struct can be used in initializing a usart_config
 * struct.
 *
 * CHANNEL is the DMA channel to be used for reception.  This must be a valid
 * DMA channel for the USART peripheral and any alternate channel mappings must
 * be handled by the board specific code.
 *
 * FIFO_SIZE is the number of bytes (which does not need to be a power of two)
 * to use for the DMA circular buffer.  This buffer must be large enough to
 * hide the worst case interrupt latency the system will encounter.  The DMA
 * RX driver adds to the output of the usart_info command a high water mark
 * of how many bytes were transferred out of this FIFO on any one interrupt.
 * This value can be used to correctly size the FIFO by setting the FIFO_SIZE
 * to something large, stress test the USART, and run usart_info.  After a
 * reasonable stress test the "DMA RX max_bytes" value will be a reasonable
 * size for the FIFO (perhaps +10% for safety).
 */
#define USART_RX_DMA(CHANNEL, FIFO_SIZE)				\
	((struct usart_rx_dma const) {					\
		.usart_rx = {						\
			.producer_ops = {				\
				.read = NULL,				\
			},						\
									\
			.init      = usart_rx_dma_init,			\
			.interrupt = usart_rx_dma_interrupt,		\
			.info      = USART_RX_DMA_INFO,			\
		},							\
									\
		.state       = &((struct usart_rx_dma_state) {}),	\
		 .fifo_buffer = ((uint8_t[FIFO_SIZE]) {}),		\
		 .fifo_size   = FIFO_SIZE,				\
		 .channel     = CHANNEL,				\
	})

/*
 * In RAM state required to manage DMA based transmission.
 */
struct usart_rx_dma_state {
	/*
	 * Previous value of dma_bytes_done.  This will wrap when the DMA fills
	 * the queue.
	 */
	size_t index;

	/*
	 * Maximum number of bytes transferred in any one RX interrupt.
	 */
	uint32_t max_bytes;
};

/*
 * Extension of the usart_rx struct to include required configuration for
 * DMA based transmission.
 */
struct usart_rx_dma {
	struct usart_rx usart_rx;

	struct usart_rx_dma_state volatile *state;

	uint8_t *fifo_buffer;
	size_t  fifo_size;

	enum dma_channel channel;
};

/*
 * Function pointers needed to initialize a usart_rx struct.  These shouldn't
 * be called in any other context as they assume that the producer or config
 * that they are passed was initialized with a complete usart_rx_dma struct.
 */
void usart_rx_dma_init(struct usart_config const *config);
void usart_rx_dma_interrupt(struct usart_config const *config);

/*
 * Debug function, used to print DMA RX statistics to the console.
 */
void usart_rx_dma_info(struct usart_config const *config);

#endif /* __CROS_EC_USART_RX_DMA_H */
