/*
* cpu_stm32g4xx_NUCLEO_HAL.c
*
* Copyright (c) 2017-2019 emotas embedded communication GmbH
*-------------------------------------------------------------------
* $Id: cpu_stm32g4xx_NUCLEO_HAL.c 38116 2021-10-04 10:22:35Z hil $
*
*
*-------------------------------------------------------------------
*
*
*/

/********************************************************************/
/**
* \file
* \brief CPU driver for Arm Cortex-M4 from STM32 with MCAN (e.g. STM32G431)
*
* This module contains the CPU specific routines for initialization
* and timer handling,
* as well as the GPIO initialization for the CAN controller
*
* Note: Adapt this initialization according to your requirements!
*
* \author emotas embedded communication GmbH
*
*/

/* header of standard C - libraries
---------------------------------------------------------------------------*/

/* header of project specific types
---------------------------------------------------------------------------*/
#include <gen_define.h>

#include <co_datatype.h>
#include <co_timer.h>
#include <co_drv.h>

/* hardware header
---------------------------------------------------------------------------*/
#include <stm32g4xx.h>
#include <stm32g4xx_hal.h>
#include <cpu_stm32.h>
#include <codrv_mcan.h>

/* constant definitions
---------------------------------------------------------------------------*/
/* OS related default definition */
#ifdef CO_OS_SIGNAL_TIMER
#else /* CO_OS_SIGNAL_TIMER */
#  define CO_OS_SIGNAL_TIMER
#endif /* CO_OS_SIGNAL_TIMER */

/* local defined data types
---------------------------------------------------------------------------*/

/* list of external used functions, if not in headers
---------------------------------------------------------------------------*/

/* list of global defined functions
---------------------------------------------------------------------------*/
__weak void SystemClock_Config(void);
void codrvTimerISR( void);
void codrvInitDebugHardware(void);
void FDCAN1_IT0_IRQHandler(void);
void FDCAN2_IT0_IRQHandler(void);
void FDCAN3_IT0_IRQHandler(void);

/* list of local defined functions
---------------------------------------------------------------------------*/


/* external variables
---------------------------------------------------------------------------*/

/* global variables
---------------------------------------------------------------------------*/

/* local defined variables
---------------------------------------------------------------------------*/


/***************************************************************************/
/**
* \brief codrvInitHardware - general customer hardware initialization
*
*/
void codrvHardwareInit(
		void	/* no parameter */
	)
{
#ifdef CUBE_MX_GENERATION
	/* initialize NVIC controller */
	NVIC_Configuration();
#else /* CUBE_MX_GENERATION */
	/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
	HAL_Init();

	/* Configure the system clock */
	SystemClock_Config();

	/* initialize NVIC controller */
	NVIC_Configuration();

	/* CAN related hardware initialization */
	codrvHardwareCanInit();
#endif /* CUBE_MX_GENERATION */
}


/***************************************************************************/
/**
* \brief codrvHardwareCanInit - CAN related hardware initialization
*
*/
void codrvHardwareCanInit(
		void	/* no parameter */
	)
{
#ifdef CUBE_MX_GENERATION
#else /* CUBE_MX_GENERATION */
GPIO_InitTypeDef  GPIO_InitStruct;

	/* GPIO Ports Clock Enable */
	__HAL_RCC_SYSCFG_CLK_ENABLE();
	__HAL_RCC_PWR_CLK_ENABLE();

	__HAL_RCC_GPIOF_CLK_ENABLE();
	
	/* Peripheral clock enable */
	__HAL_RCC_FDCAN_CLK_ENABLE();

#if defined(CONFIG_MCAN0)
	/* GPIO Ports Clock Enable */
	__HAL_RCC_GPIOB_CLK_ENABLE();

	/**FDCAN1 GPIO Configuration    
	PB8-BOOT0     ------> FDCAN1_RX
	PB9     ------> FDCAN1_TX 
	*/
	GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	GPIO_InitStruct.Alternate = GPIO_AF9_FDCAN1;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
#endif /* defined(CONFIG_MCAN0) */

#if defined(CONFIG_MCAN1)
	/* GPIO Ports Clock Enable */
	__HAL_RCC_GPIOB_CLK_ENABLE();

    /**FDCAN2 GPIO Configuration    
    PB5     ------> FDCAN2_RX
    PB6     ------> FDCAN2_TX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF9_FDCAN2;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
#endif /* defined(CONFIG_MCAN1) */

#if defined(CONFIG_MCAN2) /* not implemented at the moment! */
	/* GPIO Ports Clock Enable */
	__HAL_RCC_GPIOB_CLK_ENABLE();

	/**FDCAN3 GPIO Configuration    
	PB3     ------> FDCAN3_RX
	PB4     ------> FDCAN3_TX 
	*/
	GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	GPIO_InitStruct.Alternate = GPIO_AF11_FDCAN3;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
#endif /* defined(CONFIG_MCAN2) */

#endif /* CUBE_MX_GENERATION */
}


/***************************************************************************/
/**
* \brief codrvCanEnableInterrupt - enable CAN interrupts
*
*/
void codrvCanEnableInterrupt(
		CO_LINE_TYPE	CO_LINE		/* can line */
	)
{
	/* enable CAN interrupts */
	switch (canLine)  {
#if defined(CONFIG_MCAN0)
	case 0u:
		HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, 5, 0);
		HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn);
		break;
#endif /* defined(CONFIG_MCAN0) */
#if defined(CONFIG_MCAN1)
	case 1u:
		HAL_NVIC_SetPriority(FDCAN2_IT0_IRQn, 5, 0);
		HAL_NVIC_EnableIRQ(FDCAN2_IT0_IRQn);
		break;
#endif /* defined(CONFIG_MCAN1) */
#if defined(CONFIG_MCAN2)
	case 2u:
		HAL_NVIC_EnableIRQ(FDCAN3_IT0_IRQn);
		break;
#endif /* defined(CONFIG_MCAN2) */
	default:
		break;
	}
}


/***************************************************************************/
/**
* \brief codrvCanDisableInterrupt - disable CAN interrupts
*
*/
void codrvCanDisableInterrupt(
		CO_LINE_TYPE	CO_LINE		/* can line */
	)
{
	/* disable CAN interrupts */
	switch (canLine)  {
#if defined(CONFIG_MCAN0)
	case 0u:
		HAL_NVIC_DisableIRQ(FDCAN1_IT0_IRQn);
		break;
#endif /* defined(CONFIG_MCAN0) */
#if defined(CONFIG_MCAN1)
	case 1u:
		HAL_NVIC_DisableIRQ(FDCAN2_IT0_IRQn);
		break;
#endif /* defined(CONFIG_MCAN1) */
#if defined(CONFIG_MCAN2)
	case 2u:
		HAL_NVIC_DisableIRQ(FDCAN3_IT0_IRQn);
		break;
#endif /* defined(CONFIG_MCAN2) */
	default:
		break;
	}
}


/***************************************************************************/
/**
* \brief codrvSetCanTxInterrupt - set pending bit of the Transmit interrupt
*
* This function set the interrupt pending bit. In case of the NVIC
* enable interrupt and the CAN specific enable TX Interrupt mask
* the CAN interrupt handler is calling.
*
*/
void codrvCanSetTxInterrupt(
		CO_LINE_TYPE	CO_LINE		/* can line */
	)
{
	/* set pending bit of the CAN transmit interrupt */
	switch (canLine)  {
#if defined(CONFIG_MCAN0)
	case 0u:
		HAL_NVIC_SetPendingIRQ(FDCAN1_IT0_IRQn);
		break;
#endif /* defined(CONFIG_MCAN0) */
#if defined(CONFIG_MCAN1)
	case 1u:
		HAL_NVIC_SetPendingIRQ(FDCAN2_IT0_IRQn);
		break;
#endif /* defined(CONFIG_MCAN1) */
#if defined(CONFIG_MCAN2)
	case 2u:
		HAL_NVIC_SetPendingIRQ(FDCAN3_IT0_IRQn);
		break;
#endif /* defined(CONFIG_MCAN2) */
	default:
		break;
	}
}


#if defined(CONFIG_MCAN0)
/***************************************************************************/
/**
* \brief FDCAN1_IT0_IRQHandler - FDCAN1 interrupt line 0
*
*/
void FDCAN1_IT0_IRQHandler(
		void	/* no parameter */
	)
{
	codrvCanTransmitInterrupt(0u);
	codrvCanReceiveInterrupt(0u);
	codrvCanErrorInterrupt(0u);
}
#endif /* defined(CONFIG_MCAN0) */


#if defined(CONFIG_MCAN1)
/***************************************************************************/
/**
* \brief FDCAN2_IT0_IRQHandler - FDCAN2 interrupt line 0
*
*/
void FDCAN2_IT0_IRQHandler(
		void	/* no parameter */
	)
{
	codrvCanTransmitInterrupt(1u);
	codrvCanReceiveInterrupt(1u);
	codrvCanErrorInterrupt(1u);
}
#endif /* defined(CONFIG_MCAN1) */


#if defined(CONFIG_MCAN2)
/***************************************************************************/
/**
* \brief FDCAN3_IT0_IRQHandler - FDCAN3 interrupt line 0
*
*/
void FDCAN3_IT0_IRQHandler(
		void	/* no parameter */
	)
{
	codrvCanTransmitInterrupt(2u);
	codrvCanReceiveInterrupt(2u);
	codrvCanErrorInterrupt(2u);
}
#endif /* defined(CONFIG_MCAN2) */


/***************************************************************************/
/**
* \brief codrvTimerISR - Timer interrupt service routine
*
* is normally called from timer interrupt or from an other system timer
*
* \param
*	none
* \results
*	none
*/
void codrvTimerISR(
		void
	)
{
	/* inform stack about new timer event */
	coTimerTick();

	/* signal in case of use of an OS */
	CO_OS_SIGNAL_TIMER
}


#ifdef CODRV_USE_OS
#else /* CODRV_USE_OS */
/***************************************************************************/
/**
* \brief codrvTimerSetup - init Timer
*
* Start a cyclic hardware timer to provide timing interval.
* Alternatively it can be derived from an other system timer
* with the interval given from the DeviceDesigner.
*
*/
RET_T codrvTimerSetup(
		UNSIGNED32 timerInterval
	)
{
UNSIGNED32 clockFreqHz;

	/* Configure the Systick */
	HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

	/* get the HCLK frequency */
	clockFreqHz = HAL_RCC_GetHCLKFreq();

	/* Configure the Systick interrupt time */
	if (clockFreqHz >= (1000ul*1000ul))  {
		if (HAL_SYSTICK_Config(
			((clockFreqHz/(1000ul*1000ul)))*timerInterval) == 0u)  {
			return(RET_OK);
		}
	}

	/* HCLK frequency under 1Mhz not supported */
	return(RET_HARDWARE_ERROR);
}


/***************************************************************************/
/**
* HAL_SYSTICK_Callback - SYSTICK interrupt callback
*
*/
void HAL_SYSTICK_Callback(
		void	/* no parameter */
	)
{
	/* call timer interrupt service routine */
	codrvTimerISR();
}
#endif /* CODRV_USE_OS */


/***************************************************************************/
/**
* NVIC_Configuration - initialize NVIC controller
*
* Possible, that also the interrupt table is moving in the RAM
* (required for Boot Loader)
*
*/
void NVIC_Configuration(
		void	/* no parameter */
	)
{
	UNSIGNED8 canLine;
#ifdef CODRV_USE_OS
#else /* CODRV_USE_OS */
const UNSIGNED32 canPriority = 1u;
const UNSIGNED32 canSubPriority = 1u;


	/* CAN-FD interrupts
	 *-------------------------------------------------------*/
#if defined(CONFIG_MCAN0)
	HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, canPriority, canSubPriority);
#endif /* defined(CONFIG_MCAN0) */
#if defined(CONFIG_MCAN1)
	HAL_NVIC_SetPriority(FDCAN2_IT0_IRQn, canPriority, canSubPriority);
#endif /* defined(CONFIG_MCAN1) */
#if defined(CONFIG_MCAN2)
	HAL_NVIC_SetPriority(FDCAN3_IT0_IRQn, canPriority, canSubPriority);
#endif /* defined(CONFIG_MCAN2) */
#endif /* CODRV_USE_OS */
	for (canLine = 0; canLine < CO_MULTI_LINE_CNT; canLine++)  {
		/* enable CAN-FD interrupts */
		codrvCanEnableInterrupt(CO_LINE);
	}

#if defined(BOOT)
	/*
	 * configure the Vector table
	 * At this point very late. Better place is system_stm32fxxx.c.
	 * Correct VECT_TAB_OFFSET.
	 *
	 * NOTE: There are some reserved bits. Means, not all addresses are allowed!
	 */

#if   defined ( __CC_ARM )
/*------------------ RealView Compiler -----------------*/
	extern UNSIGNED32 __Vectors;
	SCB->VTOR = (UNSIGNED32)&__Vectors;
#endif /* defined ( __CC_ARM ) */

#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
/*------------------ ARM Compiler V6 -------------------*/
#warning not implemented yet!
#endif /* defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) */

#if defined ( __GNUC__ )
/*------------------ GNU Compiler ----------------------*/
	extern UNSIGNED32 g_pfnVectors;
	SCB->VTOR = (UNSIGNED32)&g_pfnVectors;
#endif /* defined ( __GNUC__ ) */

#if defined ( __ICCARM__ )
/*------------------ ICC Compiler ----------------------*/
#warning not implemented yet!
#endif /* defined ( __ICCARM__ ) */

#if defined ( __TMS470__ )
/*------------------ TI CCS Compiler -------------------*/
#warning not implemented yet!
#endif /* defined ( __TMS470__ ) */

#if defined ( __TASKING__ )
/*------------------ TASKING Compiler ------------------*/
#warning not implemented yet!
#endif /* defined ( __TASKING__ ) */

#if defined ( __CSMC__ )
/*------------------ COSMIC Compiler -------------------*/
#warning not implemented yet!
#endif /* defined ( __CSMC__ ) */
#endif /* defined(BOOT) */
}


#ifdef CUBE_MX_GENERATION
#else /* CUBE_MX_GENERATION */
/***************************************************************************/
/**
* SystemClock_Config - System Clock Configuration
*
*/
__weak void SystemClock_Config(
		void	/* no parameter */
	)
{
/* HCKL: 120MHz
 * CAN 40Mhz
 */
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;

	/* Configure the main internal regulator output voltage */
	HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

	/* Initializes the CPU, AHB and APB busses clocks */
	RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
	RCC_OscInitStruct.HSEState = RCC_HSE_ON;
	RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
	RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
	RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV3;
	RCC_OscInitStruct.PLL.PLLN = 30;
	RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
	RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV6;
	RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
	if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
	{
		return;
	}

	/* Initializes the CPU, AHB and APB busses clocks */
	RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
							  |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
	RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
	RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
	RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
	RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

	if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
	{
		return;
	}

	PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;
	PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL;
	if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
	{
		return;
	}

#ifdef CODRV_USE_OS
#else /* CODRV_USE_OS */
	/* Configure the Systick interrupt time */
	HAL_SYSTICK_Config(SystemCoreClock/1000);

	/* Configure the Systick */
	HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

	/* SysTick_IRQn interrupt configuration */
	HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
#endif /* CODRV_USE_OS */
}
#endif /* CUBE_MX_GENERATION */
