/* Copyright 2013 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.
 *
 * DMA interface
 */

#ifndef __CROS_EC_DMA_H
#define __CROS_EC_DMA_H

#ifdef CONFIG_DMA

#include "common.h"
#include "registers.h"

#include <stdbool.h>

/* DMA channel options */
struct dma_option {
	enum dma_channel channel;	/* DMA channel */
	void *periph;		/* Pointer to peripheral data register */
	unsigned flags;		/* DMA flags for the control register. Normally
				   used to select memory size. */
};

#define DMA_POLLING_INTERVAL_US	100	/* us */
#define DMA_TRANSFER_TIMEOUT_US	(100 * MSEC) /* us */

/**
 * Get a pointer to a DMA channel.
 *
 * @param channel	Channel to read
 * @return pointer to DMA channel registers
 */
dma_chan_t *dma_get_channel(enum dma_channel channel);

/**
 * Prepare a DMA transfer to transmit data from memory to a peripheral
 *
 * Call dma_go() afterwards to actually start the transfer.
 *
 * @param option	DMA channel options
 * @param count		Number of bytes to transfer
 * @param memory	Pointer to memory address
 *
 * @return pointer to prepared channel
 */
void dma_prepare_tx(const struct dma_option *option, unsigned count,
		    const void *memory);

/**
 * Start a DMA transfer to receive data to memory from a peripheral
 *
 * @param option	DMA channel options
 * @param count		Number of bytes to transfer
 * @param memory	Pointer to memory address
 */
void dma_start_rx(const struct dma_option *option, unsigned count,
		  void *memory);

/**
 * Stop a DMA transfer on a channel
 *
 * Disable the DMA channel and immediate stop all transfers on it.
 *
 * @param channel	Channel to stop
 */
void dma_disable(enum dma_channel channel);

/* Stop transfers on all DMA channels */
void dma_disable_all(void);

/**
 * Get the number of bytes available to read, or number of bytes written
 *
 * Since the DMA controller counts downwards, if we know the starting value
 * we can work out how many bytes have been completed so far.
 *
 * @param chan		DMA channel to check, from dma_get_channel()
 * @param orig_count	Original number of bytes requested on channel
 * @return number of bytes completed on a channel
 */
int dma_bytes_done(dma_chan_t *chan, int orig_count);

/**
 * Check if DMA for a given channel is enabled.
 *
 * @param chan  DMA channel to check, from dma_get_channel()
 * @return true if DMA is enabled on the channel, false otherwise
 */
bool dma_is_enabled(dma_chan_t *chan);

/**
 * Start a previously-prepared dma channel
 *
 * @param chan	Channel to start, from dma_get_channel()
 */
void dma_go(dma_chan_t *chan);

#ifdef CONFIG_DMA_HELP
/**
 * Testing: Print out the data transferred by a channel
 *
 * @param channel	Channel to read
 * @param buff		Start of DMA buffer
 */
void dma_check(enum dma_channel channel, char *buf);

/**
 * Dump out imformation about a dma channel
 *
 * @param channel	Channel to read
 */
void dma_dump(enum dma_channel channel);

/**
 * Testing: Test that DMA works correctly for memory to memory transfers
 */
void dma_test(enum dma_channel channel);
#endif  /* CONFIG_DMA_HELP */

/**
 * Clear the DMA interrupt/event flags for a given channel
 *
 * @param channel	Which channel's isr to clear
 */
void dma_clear_isr(enum dma_channel channel);

/**
 * Enable "Transfer Complete" interrupt for a DMA channel.
 * Will Wake up calling task when complete.
 *
 * @param channel	Which channel's interrupts to change
 */
void dma_enable_tc_interrupt(enum dma_channel channel);

/**
 * Enable "Transfer Complete" interrupt for a DMA channel with callback
 * NOTE: The callback is run at highest interrupt priority so should be
 * fast and not depend on get_time().
 *
 * @param channel	Which channel's interrupts to change
 * @param callback	Pointer to callback function to call on interrupt
 * @param callback_data Data to pass through to callback function
 */
void dma_enable_tc_interrupt_callback(enum dma_channel channel,
				      void (*callback)(void *),
				      void *callback_data);

/**
 * Disable "Transfer Complete" interrupt for a DMA channel
 *
 * @param channel	Which channel's interrupts to change
 */
void dma_disable_tc_interrupt(enum dma_channel channel);

/**
 * Wait for the DMA transfer to complete by polling the transfer complete flag
 *
 * @param channel	Channel number to wait on
 * @return EC_ERROR_TIMEOUT for timeout, EC_SUCCESS for success
 */
int dma_wait(enum dma_channel channel);

/**
 * Initialize the DMA module.
 */
void dma_init(void);

#endif /* CONFIG_DMA */
#endif
