/**
 * \file
 *
 * \brief DMA Controller Interface
 *
 * A DMA Controller (DMAC) is a device capable of doing data transfers
 * between memory and a peripheral device without CPU intervention.
 * Some controllers also support memory-to-memory transfers.
 *
 * - Compiler:           IAR EWAVR32 and GNU GCC for AVR32
 * - Supported devices:  All devices
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 * Atmel AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 */
#ifndef DMA_CONTROLLER_H_INCLUDED
#define DMA_CONTROLLER_H_INCLUDED

#include <dma.h>
#include <slist.h>
#include <types.h>
#include <buffer.h>

#include <chip/dma_controller.h>

struct buffer;

struct dmac_channel;

/**
 * \brief Peripheral register width
 */
enum dmac_reg_width {
	DMAC_REG_WIDTH_8BIT	= 0,	//!< 8 bits
	DMAC_REG_WIDTH_16BIT	= 1,	//!< 16 bits
	DMAC_REG_WIDTH_32BIT	= 2,	//!< 32 bits
};

/**
 * \brief Transaction burst length
 */
enum dmac_burst_length {
	DMAC_BURST_LENGTH_1	= 0,	//!< 1 item
	DMAC_BURST_LENGTH_4	= 1,	//!< 4 items
	DMAC_BURST_LENGTH_8	= 2,	//!< 8 items
	DMAC_BURST_LENGTH_16	= 3,	//!< 16 items
	DMAC_BURST_LENGTH_32	= 4,	//!< 32 items
};

/**
 * \brief Asynchronous DMA request
 */
struct dmac_request {
	/** \brief List of buffers associated with this request */
	struct slist		buf_list;
	/** \brief List node for use by the DMAC driver */
	struct slist_node	node;
	/** \brief Direction of the transfer (to or from the peripheral) */
	enum dma_direction	direction;
	/** \brief Peripheral register width */
	enum dmac_reg_width	reg_width;
	/** \brief Burst Transaction length */
	enum dmac_burst_length burst_length;
	/**
	 * \brief Callback called when the request is done
	 * \param chan Channel on which the request was performed
	 * \param req The request
	 */
	void			(*req_done)(struct dmac_channel *chan,
					struct dmac_request *req);
	/** \brief Arbitrary data for use by the client */
	void			*context;
	/** \brief Status indicating success or failure */
	int			status;
	/** \brief The number of bytes that were successfully transfered */
	size_t			bytes_xfered;
};

/**
 * \brief A DMA Controller Channel
 */
struct dmac_channel {
	void	(*submit_req)(struct dmac_channel *chan,
			struct dmac_request *req);
	void	(*reset)(struct dmac_channel *chan);
	/** \brief The maximum size of each buffer associated with a request */
	size_t	max_buffer_size;
};

/**
 * \brief A DMA Controller
 */
struct dma_controller {
	struct dmac_channel	*(*alloc_chan)(struct dma_controller *dmac,
					enum dmac_periph_id rx_periph,
					enum dmac_periph_id tx_periph,
					phys_addr_t rx_reg_addr,
					phys_addr_t tx_reg_addr);
	void			(*free_chan)(struct dma_controller *dmac,
					struct dmac_channel *chan);
};

/**
 * \brief Allocate a channel on a given DMA Controller
 *
 * \param dmac The DMA Controller on which to allocate a channel
 * \param rx_periph Perpiheral ID to use when receiving data, or
 * #DMAC_PERIPH_NONE if the channel is to be used for TX only.
 * \param tx_periph Perpiheral ID to use when transmitting data, or
 * #DMAC_PERIPH_NONE if the channel is to be used for RX only.
 * \param rx_reg_addr Physical address of the Receive Data Register
 * \param tx_reg_addr Physical address of the Transmit Data Register
 *
 * \return A DMA channel object which can be used to submit DMA transfer
 * requests, or NULL if no channels are available.
 */
static inline struct dmac_channel *dmac_alloc_channel(
		struct dma_controller *dmac,
		enum dmac_periph_id rx_periph,
		enum dmac_periph_id tx_periph,
		phys_addr_t rx_reg_addr,
		phys_addr_t tx_reg_addr)
{
	return dmac->alloc_chan(dmac, rx_periph, tx_periph,
			rx_reg_addr, tx_reg_addr);
}

/**
 * \brief Free a previously allocated DMA Controller channel
 * \param dmac The DMA Controller from which the channel was allocated
 * \param chan The Channel to be freed
 */
static inline void dmac_free_channel(struct dma_controller *dmac,
		struct dmac_channel *chan)
{
	dmac->free_chan(dmac, chan);
}

/**
 * \brief Submit a DMA transfer request
 *
 * This function does not return any value indicating if the request was
 * successful; set up a req_done() callback and check dmac_request::status to
 * verify the status of the request.
 *
 * \param chan The channel on which to submit the request
 * \param req The request to be submitted
 */
static inline void dmac_chan_submit_request(struct dmac_channel *chan,
		struct dmac_request *req)
{
	chan->submit_req(chan, req);
}

/**
 * \brief Reset a DMA channel
 *
 * This function stops the DMA Controller and terminates all pending requests.
 *
 * \param chan The Channel to be reset
 */
static inline void dmac_chan_reset(struct dmac_channel *chan)
{
	chan->reset(chan);
}

/**
 * \brief Initialize a DMA request
 * \param req The request to be initialized
 */
static inline void dmac_req_init(struct dmac_request *req)
{
	slist_init(&req->buf_list);
}

/**
 * \brief Assign the buffer \a buf to the DMA request \a req
 *
 * \a buf will be transfered after any previously assigned buffers.
 */
static inline void dmac_req_add_buffer(struct dmac_request *req,
		struct buffer *buf)
{
	slist_insert_tail(&req->buf_list, &buf->node);
}

#define dmac_req_for_each_buffer(req, buf)			\
	slist_for_each(&req->buf_list, buf, node)

#define dmac_req_for_each_buffer_safe(req, buf, buf_next)	\
	slist_for_each_safe(&req->buf_list, buf, buf_next, node)

#endif /* DMA_CONTROLLER_H_INCLUDED */
