/**
 * @brief       GPIO Primitive
 * @author      AXELL CORPORATION
 * @description GPIO Primitive Layer
 * @note        none 
 * @history     2017_02_22  初版
 * @history     2017_10_26  Ver2.0
 * @history     2021_01_29  [SDK3.2] GPIOポートの排他制御を有効にしたときのアセンブラルーチンを修正 (#2844)
*/
/* DOM-IGNORE-BEGIN */
/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2021 AXELL CORPORATION, all rights reserved.
 */
/* DOM-IGNORE-END */

#include "AG903_errno.h"
#include "AG903_common.h"
#include "gpio/gpioprm.h"
#include "register/AG903_gpioreg.h"


extern void GPIOPrm_ModifyBit(uintptr_t addr, uint32_t set, uint32_t clr);

/**
 * @brief           GPIOnDTOUTのレジスタWrite(8ビット単位)
 * @param           ch [in] Portチャネル
 * @param           offset [in] バイトオフセット
 * @param           data [in] データ
 * @return          void
 * @description     GPIOnDTOUTのレジスタWrite(8ビット単位)
 */
void AG903_GPIOPrmSetByteData(uint8_t ch, uint8_t offset, uint8_t data)
{
	uint8_t* reg;

	ASSERT(AG903_GPIO_CH_NUM > ch);
	ASSERT((AG903_GPIO_PORTBIT_NUM/8) > offset);	/* 8ビット単位 */

	reg  	= (uint8_t*)&AG903_GPIOn(ch)->DTOUT;
	reg    += offset;
	(*reg) 	= data;
	return;
}

/**
 * @brief           GPIOnDTOUTのレジスタRead(8ビット単位)
 * @param           ch [in] Portチャネル
 * @param           offset [in] バイトオフセット
 * @param           data [out] データ
 * @return          void
 * @description     GPIOnDTOUTのレジスタRead(8ビット単位)
 */
void AG903_GPIOPrmGetByteData(uint8_t ch, uint8_t offset, uint8_t* data)
{
	uint8_t* reg;

	ASSERT(AG903_GPIO_CH_NUM > ch);
	ASSERT((AG903_GPIO_PORTBIT_NUM/8) > offset);	/* 8ビット単位 */

	reg  	= (uint8_t*)&AG903_GPIOn(ch)->DTIN;
	reg    += offset;
	(*data)	= (*reg);
	return;
}

/**
 * @brief           GPIOnDTOUTのレジスタWrite(ビット単位)
 * @param           ch [in] Portチャネル
 * @param           set [in] セット対象（ビット指定）
 * @param           clr [in] クリア対象（ビット指定）
 * @param           ext [in] 排他制御の有無
 * @return          void
 * @description     GPIOnDTOUTのレジスタWrite(ビット単位)
 */
void AG903_GPIOPrmModifyBit(uint8_t ch, uint32_t set, uint32_t clr, _Bool ext)
{
	uint32_t reg;

	ASSERT(AG903_GPIO_CH_NUM > ch);

	if(true == ext) {
		GPIOPrm_ModifyBit((uintptr_t)(&AG903_GPIOn(ch)->DTOUT), set, ~clr);
	}
	else {
		reg = AG903_GPIOn(ch)->DTOUT;
		reg |= set;
		reg &= ~clr;
		AG903_GPIOn(ch)->DTOUT = reg;
	}

	return;
}

/**
 * @brief           GPIOnDTOUTのレジスタRead
 * @param           ch [in] Portチャネル
 * @param           data [out] 取得データ
 * @return          void
 * @description     GPIOnDTOUTのレジスタRead
 */
void AG903_GPIOPrmGetPortData(uint8_t ch, uint32_t* data)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);

	(*data) = AG903_GPIOn(ch)->DTIN;
	return;
}

/**
 * @brief           GPIOnDIRのレジスタWrite
 * @param           ch [in] Portチャネル
 * @param           dir [in] 入出力（0:入力, 1:出力）
 * @return          void
 * @description     GPIOnDIRのレジスタWrite
 */
void AG903_GPIOPrmSetDirection(uint8_t ch, uint32_t dir)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);
	
	AG903_GPIOn(ch)->DIR = dir;
	return;
}

/**
 * @brief           GPIOnDIRのレジスタRead
 * @param           ch [in] Portチャネル
 * @param           dir [out] 入出力（0:入力, 1:出力）
 * @return          void
 * @description     GPIOnDIRのレジスタRead
 */
void AG903_GPIOPrmGetDirection(uint8_t ch, uint32_t* dir)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);
	
	(*dir) = AG903_GPIOn(ch)->DIR;
	return;
}

/**
 * @brief           GPIOnIOTYPEのレジスタWrite
 * @param           ch [in] Portチャネル
 * @param           type [in] タイプ（0:通常, 1:オープンドレイン）
 * @return          void
 * @description     GPIOnIOTYPEのレジスタWrite
 */
void AG903_GPIOPrmSetType(uint8_t ch, uint32_t type)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);
	
	AG903_GPIOn(ch)->IOTYPE = type;
	return;
}

/**
 * @brief           GPIOnIOTYPEのレジスタRead
 * @param           ch [in] Portチャネル
 * @param           type [out] タイプ（0:通常, 1:オープンドレイン）
 * @return          void
 * @description     GPIOnIOTYPEのレジスタRead
 */
void AG903_GPIOPrmGetType(uint8_t ch, uint32_t* type)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);
	
	(*type) = AG903_GPIOn(ch)->IOTYPE;
	return;
}

/**
 * @brief           GPIOnPOSEDGEのレジスタWrite
 * @param           ch [in] Portチャネル
 * @param           enable [in] 検出設定（0:無効, 1:有効）
 * @return          void
 * @description     GPIOnPOSEDGEのレジスタWrite
 */
void AG903_GPIOPrmSetPosEdgeDetect(uint8_t ch, uint32_t enable)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);
	
	AG903_GPIOn(ch)->POSEDGE = enable;
	return;
}

/**
 * @brief           GPIOnPOSEDGEのレジスタRead
 * @param           ch [in] Portチャネル
 * @param           enable [out] 検出設定（0:無効, 1:有効）
 * @return          void
 * @description     GPIOnPOSEDGEのレジスタRead
 */
void AG903_GPIOPrmGetPosEdgeDetect(uint8_t ch, uint32_t* enable)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);
	
	(*enable) = AG903_GPIOn(ch)->POSEDGE;
	return;
}

/**
 * @brief           GPIOnNEGEDGEのレジスタWrite
 * @param           ch [in] Portチャネル
 * @param           enable [in] 検出設定（0:無効, 1:有効）
 * @return          void
 * @description     GPIOnNEGEDGEのレジスタWrite
 */
void AG903_GPIOPrmSetNegEdgeDetect(uint8_t ch, uint32_t enable)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);
	
	AG903_GPIOn(ch)->NEGEDGE = enable;
	return;
}

/**
 * @brief           GPIOnNEGEDGEのレジスタRead
 * @param           ch [in] Portチャネル
 * @param           enable [out] 検出設定（0:無効, 1:有効）
 * @return          void
 * @description     GPIOnNEGEDGEのレジスタRead
 */
void AG903_GPIOPrmGetNegEdgeDetect(uint8_t ch, uint32_t* enable)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);
	
	(*enable) = AG903_GPIOn(ch)->NEGEDGE;
	return;
}

/**
 * @brief           GPIOnINTMASKのレジスタWrite
 * @param           ch [in] Portチャネル
 * @param           mask [in] マスク設定（0:無効, 1:有効）
 * @return          void
 * @description     GPIOnINTMASKのレジスタWrite
 */
void AG903_GPIOPrmSetMask(uint8_t ch, uint32_t mask)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);
	
	AG903_GPIOn(ch)->INTMASK = mask;
	return;
}

/**
 * @brief           GPIOnINTMASKのレジスタRead
 * @param           ch [in] Portチャネル
 * @param           mask [out] マスク設定（0:無効, 1:有効）
 * @return          void
 * @description     GPIOnINTMASKのレジスタRead
 */
void AG903_GPIOPrmGetMask(uint8_t ch, uint32_t* mask)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);
	
	(*mask) = AG903_GPIOn(ch)->INTMASK;
	return;
}

/**
 * @brief           GPIOnEDGESTATのレジスタRead
 * @param           ch [in] Portチャネル
 * @param           status [out] エッジ検出ステータス
 * @return          void
 * @description     GPIOnEDGESTATのレジスタRead
 */
void AG903_GPIOPrmGetEdgeDetect(uint8_t ch, uint32_t* status)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);

	(*status) = AG903_GPIOn(ch)->EDGESTAT;
	return;
}

/**
 * @brief           GPIOnEDGECLRのレジスタWrite
 * @param           ch [in] Portチャネル
 * @param           clrbit [in] 制御対象（ビット指定）
 * @return          void
 * @description     GPIOnEDGECLRのレジスタWrite
 */
void AG903_GPIOPrmClearEdgeDetect(uint8_t ch, uint32_t clrbit)
{
	ASSERT(AG903_GPIO_CH_NUM > ch);

	AG903_GPIOn(ch)->EDGECLR = clrbit;
	return;
}
