/* ----------------------------------------------------------------------------
 *         SAM Software Package License
 * ----------------------------------------------------------------------------
 * Copyright (c) 2015, Atmel Corporation
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: 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
 * 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.
 * ----------------------------------------------------------------------------
 */

/**
 * \file
 *
 * Implementation of xDMA controller (XDMAC).
 *
 */

/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "chip.h"
#include "peripherals/xdmac.h"

#include <assert.h>
#include "compiler.h"

/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/

Xdmac *xdmac_get_instance(uint32_t index)
{
	assert(index < XDMAC_CONTROLLERS);
	if (index == 0)
		return XDMAC0;
	else if (index == 1)
		return XDMAC1;
	else
		return NULL;
}

uint32_t xdmac_get_periph_id(Xdmac *xdmac)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	if (xdmac == XDMAC0)
		return ID_XDMAC0;
	else if (xdmac == XDMAC1)
		return ID_XDMAC1;
	else
		return ID_PERIPH_COUNT;
}

uint32_t xdmac_get_type(Xdmac *xdmac)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	return xdmac->XDMAC_GTYPE;
}

uint32_t xdmac_get_config(Xdmac *xdmac)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	return xdmac->XDMAC_GCFG;
}

uint32_t xdmac_get_arbiter(Xdmac *xdmac)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	return xdmac->XDMAC_GWAC;
}

void xdmac_enable_global_it(Xdmac *xdmac, uint32_t int_mask)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	xdmac->XDMAC_GIE = int_mask;
}

void xdmac_disable_global_it(Xdmac *xdmac, uint32_t int_mask)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	xdmac->XDMAC_GID = int_mask;
}

uint32_t xdmac_get_global_it_mask(Xdmac *xdmac)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	return xdmac->XDMAC_GIM;
}

uint32_t xdmac_get_global_isr(Xdmac *xdmac)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	return xdmac->XDMAC_GIS;
}

uint32_t xdmac_get_masked_global_isr(Xdmac *xdmac)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	uint32_t mask = xdmac->XDMAC_GIM;

	return xdmac->XDMAC_GIS & mask;
}

void xdmac_enable_channel(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_GE |= XDMAC_GE_EN0 << channel;
}

void xdmac_enable_channels(Xdmac *xdmac, uint8_t channel_mask)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	xdmac->XDMAC_GE = channel_mask;
}

void xdmac_disable_channel(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_GD |= XDMAC_GD_DI0 << channel;
}

void xdmac_disable_channels(Xdmac *xdmac, uint8_t channel_mask)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	xdmac->XDMAC_GD = channel_mask;
}

uint32_t xdmac_get_global_channel_status(Xdmac *xdmac)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	return xdmac->XDMAC_GS;
}

void xdmac_suspend_read_channel(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_GRS |= XDMAC_GRS_RS0 << channel;
}

void xdmac_suspend_write_channel(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_GWS |= XDMAC_GWS_WS0 << channel;
}

void xdmac_suspend_read_write_channel(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_GRWS |= XDMAC_GRWS_RWS0 << channel;
}

void xdmac_resume_read_write_channel(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_GRWR |= XDMAC_GRWR_RWR0 << channel;
}

void xdmac_software_transfer_request(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_GSWR |= (XDMAC_GSWR_SWREQ0 << channel);
}

uint32_t xdmac_get_software_transfer_status(Xdmac *xdmac)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);

	return xdmac->XDMAC_GSWS;
}

void xdmac_software_flush_request(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_GSWF |= XDMAC_GSWF_SWF0 << channel;
}

void xdmac_enable_channel_it(Xdmac *xdmac, uint8_t channel, uint32_t int_mask)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CIE = int_mask;
}

void xdmac_disable_channel_it(Xdmac *xdmac, uint8_t channel, uint32_t int_mask)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CID = int_mask;
}

uint32_t xdmac_get_channel_it_mask(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	return xdmac->XDMAC_CHID[channel].XDMAC_CIM;
}

uint32_t xdmac_get_channel_isr(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	return xdmac->XDMAC_CHID[channel].XDMAC_CIS;
}

uint32_t xdmac_get_masked_channel_isr(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	uint32_t mask = xdmac->XDMAC_CHID[channel].XDMAC_CIM;

	return xdmac->XDMAC_CHID[channel].XDMAC_CIS & mask;
}

void xdmac_set_src_addr(Xdmac *xdmac, uint8_t channel, void *addr)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CSA = (uint32_t)addr;
}

void xdmac_set_dest_addr(Xdmac *xdmac, uint8_t channel, void *addr)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CDA = (uint32_t)addr;
}

void xdmac_set_descriptor_addr(Xdmac *xdmac, uint8_t channel, void *addr,
		uint32_t ndaif)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CNDA = (((uint32_t)addr) & 0xFFFFFFFC) | ndaif;
}

void xdmac_set_descriptor_control(Xdmac *xdmac, uint8_t channel,
		uint32_t config)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CNDC = config;
}

void xdmac_set_microblock_control(Xdmac *xdmac, uint8_t channel,
		uint32_t ublen)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CUBC = ublen;
}

void xdmac_set_block_control(Xdmac *xdmac, uint8_t channel, uint32_t blen)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CBC = blen;
}

void xdmac_set_channel_config(Xdmac *xdmac, uint8_t channel, uint32_t config)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CC = config;
}

uint32_t xdmac_get_channel_config(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	return xdmac->XDMAC_CHID[channel].XDMAC_CC;
}

void xdmac_set_data_stride_mem_pattern(Xdmac *xdmac, uint8_t channel,
			       uint32_t dds_msp)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CDS_MSP = dds_msp;
}

void xdmac_set_src_microblock_stride(Xdmac *xdmac, uint8_t channel,
		uint32_t subs)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CSUS = subs;
}

void xdmac_set_dest_microblock_stride(Xdmac *xdmac, uint8_t channel,
		uint32_t dubs)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	xdmac->XDMAC_CHID[channel].XDMAC_CDUS = dubs;
}

uint32_t xdmac_get_channel_dest_addr(Xdmac *xdmac, uint8_t channel)
{
	assert(xdmac == XDMAC0 || xdmac == XDMAC1);
	assert(channel < XDMAC_CHANNELS);

	return xdmac->XDMAC_CHID[channel].XDMAC_CDA;
}
