/*
* This program was created by AXELL CORPORATION.
* Copyright (C) 2017 AXELL CORPORATION, all rights reserved.
*/
#include "sample_common.h"
#include "com.h"
#include "int/intmgr.h"
#include "sys/sscprm.h"
#include "bsc/bscprm.h"
#include "register/AG903_bscreg.h"

#define AG903_BSC_INT2_NO				(2)						/* INT2割込み番号 */
#define AG903_BSC_INT3_NO				(3)						/* INT3割込み番号 */
#define AG903_BSC_INT_ENT				(AG903_BSC_INT2_NO)		/* 対象となる割込み番号の先頭 */
#define AG903_BSC_INT_NUM				(2)						/* 対象となる割込み数*/
#define AG903_BSC_PBINTMODE_LEVEL		(0)						/* PB割込みモード(レベル検出) */
#define AG903_BSC_PBINTMODE_EDGE		(1)						/* PB割込みモード(エッジ検出) */
#define AG903_BSC_PBINTPOL_POSITIVE		(1)						/* PB割込み極性(正論理) */
#define AG903_BSC_PBINTPOL_NEGATIVE		(0)						/* PB割込み極性(負論理) */
#define AG903_BSC_PBINTMASK_MASK		(1)						/* PB割込みマスクビット */
#define AG903_BSC_PIN_INT2				(122)					/* INT2端子ドライブ能力設定テーブル番号 */
#define AG903_BSC_PIN_INT3				(123)					/* INT2端子ドライブ能力設定テーブル番号 */

/* プロトタイプ */
static int32_t Bsc_init_module(void);
static int32_t Bsc_term_module(void);
static void Bsc_SampleHelp(void);
static void Bsc_Int2Test(void);
static void Bsc_Int3Test(void);
static void Bsc_TestInt(uint8_t intnum);
static int32_t Bsc_WaitLevelDetect(uint8_t intnum);

/* 外部割込み用INTMgr */
typedef void (*AG903_GPIOMgrIntHdr)(void);
static void Bsc_Inthdr2(void);
static void Bsc_Inthdr3(void);
static const AG903_GPIOMgrIntHdr BscIntHdr[AG903_BSC_INT_NUM] = { Bsc_Inthdr2, Bsc_Inthdr3 };
static void (*func[])() = { Bsc_SampleHelp, Bsc_Int2Test, Bsc_Int3Test};

/* Macro */
#define	EPRINT(FMT, ...)		sys_print(0, FMT,##__VA_ARGS__)
#define	PRINT(FMT, ...)			sys_print(0, FMT"\r\n",##__VA_ARGS__)
#define BSC_ERROR(str)			sys_print(1, " ERROR:%s[%d] *%s*\r\n",__FUNCTION__, __LINE__, str)

/* IRQステータス */
static volatile _Bool BscDetectIrq[AG903_BSC_INT_NUM];
/* 端子ドライブ能力設定値(退避用) */
static AG903_SSCPrmPinSetup pinsetup[AG903_BSC_INT_NUM];

/**
 * @brief		メイン処理
 * @param[in]	param	制御パラメータ
 * @return		なし
 * @note		なし
 */
void BSC_main(uint8_t param)
{
	uint8_t		mode = 0;
	char		input[2+2];

	((void)param);

	Bsc_SampleHelp();	/* Help表示 */

	Bsc_init_module();

	while (mode != 0xFF) {
		EPRINT(" -- Choose sample number (00h<HELP> to FFh): ");
		COM_GetNum_Wait(input, sizeof(input));
		mode = (ASCtoBIN(input[0]) << 4)
			 | (ASCtoBIN(input[1]) << 0);
		if(0xFF == mode) {
			break;
		}
		if(mode >= (sizeof(func)/sizeof(void*))) {
			func[0](); /* Help */
		}
		else {
			func[mode]();	/* Sample func実行 */
		}
	}

	Bsc_term_module();

	return;
}

/**
 * @brief		サブ処理
 * @param[in]	param	制御パラメータ
 * @return		なし
 * @note		なし
 */
void BSC_sub(uint32_t param)
{
	((void)param);
	/* 処理なし */
	return;
}

/**
 * @brief		モジュール初期化
 * @param[in]	なし
 * @retval		AG903_ENONE		正常終了
 * @note		なし
 */
static int32_t Bsc_init_module(void)
{
	int32_t					num;
	AG903_SSCPrmPinSetup	setup;

	/* nINT2端子能力設定(DB51903Mではプルアップに設定) */
	sys_memset(&pinsetup[0], 0, sizeof(pinsetup[0]));
	AG903_SSCPrmGetPinSetup(AG903_BSC_PIN_INT2, &pinsetup[0]);
	setup.io3v		= pinsetup[0].io3v;
	setup.drive		= pinsetup[0].drive;
	setup.sr_slow	= pinsetup[0].sr_slow;
	setup.schmitt	= pinsetup[0].schmitt;
	setup.lv_set	= AG903_SSC_INPUT_ENABLE_PLUP;
	AG903_SSCPrmSetPinSetup(AG903_BSC_PIN_INT2, &setup);
	/* nINT3端子能力設定(DB51903Mではプルアップに設定) */
	sys_memset(&pinsetup[1], 0, sizeof(pinsetup[1]));
	AG903_SSCPrmGetPinSetup(AG903_BSC_PIN_INT3, &pinsetup[1]);
	setup.io3v		= pinsetup[1].io3v;
	setup.drive		= pinsetup[1].drive;
	setup.sr_slow	= pinsetup[1].sr_slow;
	setup.schmitt	= pinsetup[1].schmitt;
	setup.lv_set	= AG903_SSC_INPUT_ENABLE_PLUP;
	AG903_SSCPrmSetPinSetup(AG903_BSC_PIN_INT3, &setup);

	/* nINT2, nINT3 端子機能設定 */
	Port_SetFunction(14, PORT_FUNCTION_2);	/* グループ14 = Function2 [nINT2,nINT3] */
	Port_DisableGpio(122, 2);				/* GPIO122〜GPIO123 = Disable */

	for(num = 0; num < AG903_BSC_INT_NUM; num++){
		/* FPGA 外部割込みネゲート */
		Board_EnablePbInt(num, false);

		/* INTMng ハンドラ設定&割込み許可 */
		AG903_INTMgrHdrPrm	inthdr;
		inthdr.atr   = AG903_INT_HLNG;
		inthdr.intno = AG903_IRQ54_PBH2 + num;
		inthdr.func  = (void*)BscIntHdr[num];
		if(0 >= AG903_INTMgrSetHandler(&inthdr)) {
			return -AG903_EFAULT;
		}
		AG903_INTMgrEnableInt(AG903_IRQ54_PBH2 + num);
	}

	return AG903_ENONE;
}

/**
 * @brief		モジュール終了
 * @param[in]	なし
 * @retval		AG903_ENONE		正常終了
 * @note		なし
 */
static int32_t Bsc_term_module(void)
{
	int32_t		num;

	/* INTMng 割込み禁止 */
	for(num = 0; num < AG903_BSC_INT_NUM; num++){
		AG903_INTMgrDisableInt(AG903_IRQ54_PBH2 + num);
	}

	/* nINT2, nINT3 端子GPIO設定 */
	Port_EnableGpio(122, 2);				/* GPIO122〜GPIO123 = Enable */
	/* nINT2端子ドライブ能力を戻す */
	AG903_SSCPrmSetPinSetup(AG903_BSC_PIN_INT2, &pinsetup[0]);
	/* nINT3端子ドライブ能力を戻す */
	AG903_SSCPrmSetPinSetup(AG903_BSC_PIN_INT3, &pinsetup[1]);

	return AG903_ENONE;
}

/**
 * @brief		ヘルプ表示
 * @param		なし
 * @return		なし
 * @note		なし
 */
static void Bsc_SampleHelp(void)
{
	PRINT("\t# 00 ... Help");
	PRINT("\t# 01 ... nINT2 Test");
	PRINT("\t# 02 ... nINT3 Test");
	PRINT("\t# FF ... Exit Sample");

	return;
}

/**
 * @brief		nINT2レベル検出
 * @param		なし
 * @return		なし
 * @note		なし
 */
static void Bsc_Int2Test(void)
{
	Bsc_TestInt(2);
}

/**
 * @brief		nINT3レベル検出
 * @param		なし
 * @return		なし
 * @note		なし
 */
static void Bsc_Int3Test(void)
{
	Bsc_TestInt(3);
}

/**
 * @brief		割込みレベル検出
 * @param[in]	intnum [in] 割込み番号
 * @return		なし
 * @note		DB51903M FPGAパラレルバス割込み機能により外部割込みを生成する
 */
static void Bsc_TestInt(uint8_t intnum)
{
	uint8_t	num;
	uint8_t	mask, mode, pol;

	for(num = 0; num < AG903_BSC_INT_NUM; num++){
		BscDetectIrq[num] = false;
	}

	/* モード保存 */
	AG903_BSCPrmGetIntMode(&mode);
	mode &= (~(1 << (AG903_BSC_PBINTMODE_INT0_POS + intnum)) & 0x0F);
	/* 極性保存 */
	AG903_BSCPrmGetIntPol(&pol);
	pol  &= (~(1 << (AG903_BSC_PBINTPOL_INT0_POS  + intnum)) & 0x0F);
	/* マスク値保存 */
	AG903_BSCPrmGetIntMask(&mask);
	mask &= (~(1 << (AG903_BSC_PBINTMASK_INT0_POS + intnum)) & 0x0F);

	/* ステータスクリア */
	AG903_BSCPrmSetIntClr(1 << (AG903_BSC_PBINTCLR_INT0_POS + intnum));
	/* LEVEL検出 */
	AG903_BSCPrmSetIntMode(mode | (AG903_BSC_PBINTMODE_LEVEL << (AG903_BSC_PBINTMODE_INT0_POS + intnum)));
	/* アクティブLow */
	AG903_BSCPrmSetIntPol(pol | (AG903_BSC_PBINTPOL_NEGATIVE << (AG903_BSC_PBINTPOL_INT0_POS + intnum)));
	/* マスク解除 */
	AG903_BSCPrmSetIntMask(mask | (~((AG903_BSC_PBINTMASK_MASK << (AG903_BSC_PBINTMASK_INT0_POS + intnum))) & 0x0F));

	/* 検出〜待ち */
	PRINT(" Wait INT%d", intnum);
	/* 待機 */
	Bsc_WaitLevelDetect(intnum);
	/* 検出〜終了 */
	PRINT(" Detect INT%d", intnum);

	/* マスク設定 */
	AG903_BSCPrmSetIntMask(mask | (AG903_BSC_PBINTMASK_MASK << (AG903_BSC_PBINTMASK_INT0_POS + intnum)));

	return;
}

/**
 * @brief		レベル検出待ち
 * @param[in]	intnum	割込み番号
 * @return		なし
 * @note		DB51903M FPGAパラレルバス割込み機能により外部割込みを生成する
 */
static int32_t Bsc_WaitLevelDetect(uint8_t intnum)
{
	int32_t retval = AG903_ENONE;
	int n;
	_Bool bloop = true;

	while(bloop != false) {
		for(n = 0; n < 100; n++){
			if(BscDetectIrq[intnum - AG903_BSC_INT_ENT] != false) {
				bloop = false;
				break;
			}
			sys_dlytsk(10);
		}
		/* FPGA 外部割込みアサート */
		Board_EnablePbInt(intnum, true);
	}
	/* FPGA 外部割込みネゲート */
	Board_EnablePbInt(intnum, false);

	return retval;
}

/**
 * @brief	パラレルバスINT2割込みハンドラ.
 * @param	none
 * @return	none
 * @note	none
 */
static void Bsc_Inthdr2(void)
{
	uint8_t mask;

	/* INT2割込みを検出 */
	BscDetectIrq[AG903_BSC_INT2_NO - AG903_BSC_INT_ENT] = true;

	/* INT2割込みマスク */
	AG903_BSCPrmGetIntMask(&mask);
	mask |= (1 << (AG903_BSC_PBINTCLR_INT2_POS));
	AG903_BSCPrmSetIntMask(mask);
}
/**
 * @brief	パラレルバスINT3割込みハンドラ.
 * @param	none
 * @return	none
 * @note	none
 */
static void Bsc_Inthdr3(void)
{
	uint8_t mask;

	/* INT3割込みを検出 */
	BscDetectIrq[AG903_BSC_INT3_NO - AG903_BSC_INT_ENT] = true;

	/* INT3割込みマスク */
	AG903_BSCPrmGetIntMask(&mask);
	mask |= (1 << (AG903_BSC_PBINTCLR_INT3_POS));
	AG903_BSCPrmSetIntMask(mask);
}
