/**
 * @brief       OSP Primitive
 * @author      AXELL CORPORATION
 * @description OSP Primitive
 * @note        none
 * @history     2017_02_22  初版
 * @history     2017_10_26  Ver2.0
 * @history     2019_03_08  [SDK2.2] AG903_OSPPrmEnableEventでの設定時にOVFビットをクリアするように変更 (#2199)
 */
/* DOM-IGNORE-BEGIN */
/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2019 AXELL CORPORATION, all rights reserved.
 */
/* DOM-IGNORE-END */

#include "AG903_common.h"

#include "register/AG903_ospreg.h"
#include "osp/ospprm.h"
#include "osp/ospmgr.h"
#include "osp.h"

/** 共通レジスタ名アクセスマクロ */
#define	EVENT(R)			(AG903_OSP->R)
/** EVENTnレジスタアクセスマクロ */
#define	EVENTn(N)			*((uint32_t *)AG903_OSP + N)
/** OSPEvt_Type構造体でのアクセス用マクロ */
#define	EVTn(EVT)			((OSPEvt_Type *)(AG903_OSP_BASE + (EVT / 4) * 0x04UL))

/** OSPEVENTレジスタ構造 */
typedef struct _OSPEvt_Type {
	/** アクセス単位共用体定義 */
	union {
		uint32_t EVENT;				/** union:32bit			 */

		struct {
			uint32_t VAL0:4;		/** 0:カウンタ値		 */
			uint32_t OVF0:1;		/** 0:オーバーフロー	 */
			uint32_t EN0 :1;		/** 0:カウンタ動作有効	 */
			uint32_t	 :2;		/**						 */

			uint32_t VAL1:4;		/** 1:カウンタ値		 */
			uint32_t OVF1:1;		/** 1:オーバーフロー	 */
			uint32_t EN1 :1;		/** 1:カウンタ動作有効	 */
			uint32_t	 :2;		/**						 */
			uint32_t VAL2:4;		/** 2:カウンタ値		 */
			uint32_t OVF2:1;		/** 2:オーバーフロー	 */
			uint32_t EN2 :1;		/** 2:カウンタ動作有効	 */
			uint32_t	 :2;		/**						 */

			uint32_t VAL3:4;		/** 3:カウンタ値		 */
			uint32_t OVF3:1;		/** 3:オーバーフロー	 */
			uint32_t EN3 :1;		/** 3:カウンタ動作有効	 */
			uint32_t	 :2;		/**						 */
		};							/** union:ビット別		 */
	};
}OSPEvt_Type;

/**
 * @brief           チャンネル制御
 * @param           ch [in] チャンネル (0〜3)
 * @param           en [in] チャンネル制御 (0:無効 1:有効)
 * @return          void
 * @description     チャンネル制御設定を変更します.<p>
 *                  AG903_OSPn(ch)->CTRL にライトします.
 */
void AG903_OSPPrmCTRL(const uint8_t ch, const uint8_t en)
{
	ASSERT(ch < AG903_OSP_CH_NUM);

	AG903_OSPn(ch)->CTRL = (en << AG903_OSPn_CTRL_EN_POS) & AG903_OSPn_CTRL_EN_MSK;
}

/**
 * @brief           チャンネル優先度
 * @param           ch [in] チャンネル (0〜3)
 * @param           pri [in] チャンネル優先度(0:低 〜 3:高)
 * @return          void
 * @description     チャンネル優先度
 *                  AG903_OSPn(ch)->PRIOR にライトします.
 */
void AG903_OSPPrmPRIOR(const uint8_t ch, const uint8_t pri)
{
	ASSERT(ch < AG903_OSP_CH_NUM);
	ASSERT(pri < AG903_OSP_PRI_NUM);

	AG903_OSPn(ch)->PRIOR = (pri << AG903_OSPn_PRIOR_PRIOR_POS) & AG903_OSPn_PRIOR_PRIOR_MSK;
}

/**
 * @brief           FIFO状態
 * @param           ch [in] チャンネル (0〜3)
 * @param           ovf [out] FIFOのオーバーフロー状態 (1:オーバーフロー発生)
 * @param           siz [out] FIFOの空き容量
 * @return          void
 * @description     FIFO状態を取得します.<p>
 *                  AG903_OSPn(ch)->FIFOSTAT からリードします.
 */
void AG903_OSPPrmFIFOSTAT(const uint8_t ch, uint8_t *ovf, uint8_t *siz)
{
	uint32_t	val;

	ASSERT(ch < AG903_OSP_CH_NUM);
	ASSERT(ovf != NULL);
	ASSERT(siz != NULL);

	val  = AG903_OSPn(ch)->FIFOSTAT;
	*ovf = (val & AG903_OSPn_FIFOSTAT_OVF_MSK) >> AG903_OSPn_FIFOSTAT_OVF_POS;
	*siz = (val & AG903_OSPn_FIFOSTAT_SIZE_MSK ) >> AG903_OSPn_FIFOSTAT_SIZE_POS;
}

/**
 * @brief           FIFO制御
 * @param           ch [in] チャンネル (0〜3)
 * @return          void
 * @note            none
 * @description     FIFO制御設定を変更します.<p>
 *                  AG903_OSPn(ch)->FIFOCTRL にライトします.
 */
void AG903_OSPPrmFIFOCTRL(const uint8_t ch)
{
	ASSERT(ch < AG903_OSP_CH_NUM);

	AG903_OSPn(ch)->FIFOCTRL = AG903_OSPn_FIFOCTRL_CLR_MSK;
}

/**
 * @brief           FIFOデータ
 * @param           ch [in] チャンネル (0〜3)
 * @param           dats [in] データリスト
 * @param           len [in] データサイズ
 * @return          void
 * @description     FIFOデータに書き込みます.<p>
 *                  AG903_OSPn(ch)->FIFODT にライトします.
 */
void AG903_OSPPrmFIFODT(const uint8_t ch, const uint32_t *dats, const uint32_t len)
{
	uint32_t	ii;

	ASSERT(ch < AG903_OSP_CH_NUM);
	ASSERT(dats != NULL);

	for (ii = 0;ii < len;ii++) {
		AG903_OSPn(ch)->FIFODT = *dats++;
	}
}

/**
 * @brief           内部状態取得
 * @param           ch [in] チャンネル (0〜3)
 * @param           stat [out] 内部状態格納バッファ
 * @param           cnt [out] 現在カウント値格納バッファ
 * @return          void
 * @description     内部状態を取得します.<p>
 *                  AG903_OSPn(ch)->STAT からリードします.
 */
void AG903_OSPPrmSTAT(const uint8_t ch, uint8_t *stat, uint8_t *cnt)
{
	uint32_t	val;

	ASSERT(ch < AG903_OSP_CH_NUM);
	ASSERT(stat != NULL);
	ASSERT(cnt != NULL);

	val   = AG903_OSPn(ch)->STAT;
	*cnt  = (val & AG903_OSPn_STAT_COUNT_MSK) >> AG903_OSPn_STAT_COUNT_POS;
	*stat = (val & AG903_OSPn_STAT_STAT_MSK) >> AG903_OSPn_STAT_STAT_POS;
}

/**
 * @brief           イベント制御
 * @param           evt [in] イベント番号
 * @param           en [in] イベントイネーブル
 * @param           cnt [in] イベントカウンタ
 * @return          void
 * @description     各イベントの制御設定を変更します.
 * @note            イベントカウンタが不要な場合は0を指定してください.
 *                  カウンタのオーバーフローはクリアされます.
 */
void AG903_OSPPrmEnableEvent(const uint8_t evt, const uint8_t en, const uint8_t cnt)
{
	uint8_t evtno, regno, ecnt, ovf;

	ASSERT(evt < AG903_OSP_EVENT_NUM);

	regno = evt / 4;
	evtno = evt % 4;								/* 当該レジスタ番号取得		*/

	uint8_t *reg = (uint8_t *)&EVENTn(regno) + evtno;
	if (en == 0) {
		ecnt = (*reg & AG903_OSP_EVENT0_VAL0_MSK) >> AG903_OSP_EVENT0_VAL0_POS;
		ovf  = (*reg & AG903_OSP_EVENT0_OVF0_MSK) >> AG903_OSP_EVENT0_OVF0_POS;
	} else {
		ecnt = cnt;
		ovf  = 0;
	}
	*reg = ((en << AG903_OSP_EVENT0_EN0_POS) & AG903_OSP_EVENT0_EN0_MSK		)|
		   ((ovf  << AG903_OSP_EVENT0_OVF0_POS) & AG903_OSP_EVENT0_OVF0_MSK )|
		   ((ecnt << AG903_OSP_EVENT0_VAL0_POS) & AG903_OSP_EVENT0_VAL0_MSK	);
}

/**
 * @brief           イベント状態取得
 * @param           evt [in] イベント番号
 * @param           ovf [out] オーバーフロー状態
 * @param           cnt [out] イベントカウント
 * @return          void
 * @description     各イベントの状態を取得します.
 */
void AG903_OSPPrmGetEventStat(const uint8_t evt, uint8_t *ovf, uint8_t *cnt)
{
	uint8_t		evtno;

	ASSERT(evt < AG903_OSP_EVENT_NUM);
	ASSERT(ovf != NULL);
	ASSERT(cnt != NULL);

	evtno = evt % 4;								/* 当該レジスタ番号取得		*/

	switch (evtno) {
		case  0:
			*ovf = EVTn(evt)->OVF0;
			*cnt = EVTn(evt)->VAL0;
			break;

		case  1:
			*ovf = EVTn(evt)->OVF1;
			*cnt = EVTn(evt)->VAL1;
			break;

		case  2:
			*ovf = EVTn(evt)->OVF2;
			*cnt = EVTn(evt)->VAL2;
			break;

		case  3:
			*ovf = EVTn(evt)->OVF3;
			*cnt = EVTn(evt)->VAL3;
			break;
	}
}

/**
 * @brief           フラグセット
 * @param           flag [in] フラグ番号(ビット)
 * @return          void
 * @description     '1b'を指定したビットに該当するフラグをフラグをセットします.
 */
void AG903_OSPPrmFLAGSET(const uint32_t flag)
{
	EVENT(FLAGSET) = (flag << AG903_OSP_FLAGSET_F_POS) & AG903_OSP_FLAGSET_F_MSK;
}

/**
 * @brief           フラグクリア
 * @param           flag [in] フラグ番号(ビット)
 * @return          void
 * @description     '1b'を指定したビットに該当するフラグをクリアします.
 */
void AG903_OSPPrmFLAGCLR(const uint32_t flag)
{
	EVENT(FLAGCLR) = (flag << AG903_OSP_FLAGCLR_F_POS) & AG903_OSP_FLAGCLR_F_MSK;
}

/**
 * @brief           フラグ状態
 * @param           val [out] フラグの値(0:OFF 1:ON)
 * @return          void
 * @description     各フラグの状態を取得します.
 */
void AG903_OSPPrmFLAGSTAT(uint32_t *val)
{
	ASSERT(val != NULL);

	*val = (EVENT(FLAGSTAT) & AG903_OSP_FLAGSTAT_F_MSK) >> AG903_OSP_FLAGSTAT_F_POS;
}
