diff --git a/drivers/fsl_clock.c b/drivers/fsl_clock.c new file mode 100644 index 0000000..81cbe23 --- /dev/null +++ b/drivers/fsl_clock.c @@ -0,0 +1,2176 @@ +/* + * Copyright 2017 - 2020 , NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_clock.h" +#include "fsl_power.h" +/******************************************************************************* + * Definitions + ******************************************************************************/ +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.clock" +#endif +#define NVALMAX (0x100U) +#define PVALMAX (0x20U) +#define MVALMAX (0x10000U) + +#define PLL_MAX_N_DIV 0x100U + +/*-------------------------------------------------------------------------- +!!! If required these #defines can be moved to chip library file +----------------------------------------------------------------------------*/ + +#define PLL_SSCG1_MDEC_VAL_P (10U) /* MDEC is in bits 25 downto 10 */ +#define PLL_SSCG1_MDEC_VAL_M (0xFFFFULL << PLL_SSCG1_MDEC_VAL_P) +#define PLL_NDEC_VAL_P (0U) /* NDEC is in bits 9:0 */ +#define PLL_NDEC_VAL_M (0xFFUL << PLL_NDEC_VAL_P) +#define PLL_PDEC_VAL_P (0U) /*!< PDEC is in bits 6:0 */ +#define PLL_PDEC_VAL_M (0x1FUL << PLL_PDEC_VAL_P) + +#define PLL_MIN_CCO_FREQ_MHZ (275000000U) +#define PLL_MAX_CCO_FREQ_MHZ (550000000U) +#define PLL_LOWER_IN_LIMIT (2000U) /*!< Minimum PLL input rate */ +#define PLL_HIGHER_IN_LIMIT (150000000U) /*!< Maximum PLL input rate */ +#define PLL_MIN_IN_SSMODE (3000000U) +#define PLL_MAX_IN_SSMODE \ + (100000000U) /*!< Not find the value in UM, Just use the maximum frequency which device support */ + +/* PLL NDEC reg */ +#define PLL_NDEC_VAL_SET(value) (((unsigned long)(value) << PLL_NDEC_VAL_P) & PLL_NDEC_VAL_M) +/* PLL PDEC reg */ +#define PLL_PDEC_VAL_SET(value) (((unsigned long)(value) << PLL_PDEC_VAL_P) & PLL_PDEC_VAL_M) +/* SSCG control1 */ +#define PLL_SSCG1_MDEC_VAL_SET(value) (((uint64_t)(value) << PLL_SSCG1_MDEC_VAL_P) & PLL_SSCG1_MDEC_VAL_M) + +/* PLL0 SSCG control1 */ +#define PLL0_SSCG_MD_FRACT_P 0U +#define PLL0_SSCG_MD_INT_P 25U +#define PLL0_SSCG_MD_FRACT_M (0x1FFFFFFUL << PLL0_SSCG_MD_FRACT_P) +#define PLL0_SSCG_MD_INT_M ((uint64_t)0xFFUL << PLL0_SSCG_MD_INT_P) + +#define PLL0_SSCG_MD_FRACT_SET(value) (((uint64_t)(value) << PLL0_SSCG_MD_FRACT_P) & PLL0_SSCG_MD_FRACT_M) +#define PLL0_SSCG_MD_INT_SET(value) (((uint64_t)(value) << PLL0_SSCG_MD_INT_P) & PLL0_SSCG_MD_INT_M) + +/* Saved value of PLL output rate, computed whenever needed to save run-time + computation on each call to retrive the PLL rate. */ +static uint32_t s_Pll0_Freq; +static uint32_t s_Pll1_Freq; + +/** External clock rate on the CLKIN pin in Hz. If not used, + set this to 0. Otherwise, set it to the exact rate in Hz this pin is + being driven at. */ +static uint32_t s_Ext_Clk_Freq = 16000000U; +static uint32_t s_I2S_Mclk_Freq = 0U; +static uint32_t s_PLU_ClkIn_Freq = 0U; + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/******************************************************************************* + * Prototypes + ******************************************************************************/ +/* Find SELP, SELI, and SELR values for raw M value, max M = MVALMAX */ +static void pllFindSel(uint32_t M, uint32_t *pSelP, uint32_t *pSelI, uint32_t *pSelR); +/* Get predivider (N) from PLL0 NDEC setting */ +static uint32_t findPll0PreDiv(void); +/* Get predivider (N) from PLL1 NDEC setting */ +static uint32_t findPll1PreDiv(void); +/* Get postdivider (P) from PLL0 PDEC setting */ +static uint32_t findPll0PostDiv(void); +/* Get postdivider (P) from PLL1 PDEC setting. */ +static uint32_t findPll1PostDiv(void); +/* Get multiplier (M) from PLL0 MDEC and SSCG settings */ +static float findPll0MMult(void); +/* Get multiplier (M) from PLL1 MDEC. */ +static uint32_t findPll1MMult(void); +/* Get the greatest common divisor */ +static uint32_t FindGreatestCommonDivisor(uint32_t m, uint32_t n); +/* Set PLL output based on desired output rate */ +static pll_error_t CLOCK_GetPll0Config(uint32_t finHz, uint32_t foutHz, pll_setup_t *pSetup, bool useSS); +/* Update local PLL rate variable */ +static void CLOCK_GetPLL0OutFromSetupUpdate(pll_setup_t *pSetup); +/* Update local PLL1 rate variable */ +static void CLOCK_GetPLL1OutFromSetupUpdate(pll_setup_t *pSetup); + +/******************************************************************************* + * Code + ******************************************************************************/ + +/* Clock Selection for IP */ +/** + * brief Configure the clock selection muxes. + * param connection : Clock to be configured. + * return Nothing + */ +void CLOCK_AttachClk(clock_attach_id_t connection) +{ + uint8_t mux; + uint8_t sel; + uint16_t item; + uint32_t tmp32 = (uint32_t)connection; + uint32_t i; + volatile uint32_t *pClkSel; + + pClkSel = &(SYSCON->SYSTICKCLKSELX[0]); + + if (kNONE_to_NONE != connection) + { + for (i = 0U; i < 2U; i++) + { + if (tmp32 == 0U) + { + break; + } + item = (uint16_t)GET_ID_ITEM(tmp32); + if (item != 0U) + { + mux = (uint8_t)GET_ID_ITEM_MUX(item); + sel = (uint8_t)GET_ID_ITEM_SEL(item); + if (mux == CM_RTCOSC32KCLKSEL) + { + PMC->RTCOSC32K = (PMC->RTCOSC32K & ~PMC_RTCOSC32K_SEL_MASK) | PMC_RTCOSC32K_SEL(sel); + } + else if (mux == CM_OSTIMERCLKSEL) + { + PMC->OSTIMERr = (PMC->OSTIMERr & ~PMC_OSTIMER_OSTIMERCLKSEL_MASK) | PMC_OSTIMER_OSTIMERCLKSEL(sel); + } + else + { + pClkSel[mux] = sel; + } + } + tmp32 = GET_ID_NEXT_ITEM(tmp32); /* pick up next descriptor */ + } + } +} + +/* Return the actual clock attach id */ +/** + * brief Get the actual clock attach id. + * This fuction uses the offset in input attach id, then it reads the actual source value in + * the register and combine the offset to obtain an actual attach id. + * param attachId : Clock attach id to get. + * return Clock source value. + */ +clock_attach_id_t CLOCK_GetClockAttachId(clock_attach_id_t attachId) +{ + uint8_t mux; + uint32_t actualSel; + uint32_t tmp32 = (uint32_t)attachId; + uint32_t i; + uint32_t actualAttachId = 0U; + uint32_t selector = GET_ID_SELECTOR(tmp32); + volatile uint32_t *pClkSel; + + pClkSel = &(SYSCON->SYSTICKCLKSELX[0]); + + if (kNONE_to_NONE == attachId) + { + return kNONE_to_NONE; + } + + for (i = 0U; i < 2U; i++) + { + mux = (uint8_t)GET_ID_ITEM_MUX(tmp32); + if (tmp32 != 0UL) + { + if (mux == CM_RTCOSC32KCLKSEL) + { + actualSel = ((PMC->RTCOSC32K) & PMC_RTCOSC32K_SEL_MASK) >> PMC_RTCOSC32K_SEL_SHIFT; + } + else if (mux == CM_OSTIMERCLKSEL) + { + actualSel = ((PMC->OSTIMERr) & PMC_OSTIMER_OSTIMERCLKSEL_MASK) >> PMC_OSTIMER_OSTIMERCLKSEL_SHIFT; + } + else + { + actualSel = pClkSel[mux]; + } + + /* Consider the combination of two registers */ + actualAttachId |= CLK_ATTACH_ID(mux, actualSel, i); + } + tmp32 = GET_ID_NEXT_ITEM(tmp32); /*!< pick up next descriptor */ + } + + actualAttachId |= selector; + + return (clock_attach_id_t)actualAttachId; +} + +/* Set IP Clock Divider */ +/** + * brief Setup peripheral clock dividers. + * param div_name : Clock divider name + * param divided_by_value: Value to be divided + * param reset : Whether to reset the divider counter. + * return Nothing + */ +void CLOCK_SetClkDiv(clock_div_name_t div_name, uint32_t divided_by_value, bool reset) +{ + volatile uint32_t *pClkDiv; + + pClkDiv = &(SYSCON->SYSTICKCLKDIV0); + if ((div_name >= kCLOCK_DivFlexFrg0) && (div_name <= kCLOCK_DivFlexFrg7)) + { + /*!< Flexcomm Interface function clock = (clock selected via FCCLKSEL) / (1+ MULT /DIV), DIV = 0xFF */ + ((volatile uint32_t *)pClkDiv)[(uint8_t)div_name] = + SYSCON_FLEXFRG0CTRL_DIV_MASK | SYSCON_FLEXFRG0CTRL_MULT(divided_by_value); + } + else + { + if (reset) + { + ((volatile uint32_t *)pClkDiv)[(uint8_t)div_name] = 1UL << 29U; + } + if (divided_by_value == 0U) /*!< halt */ + { + ((volatile uint32_t *)pClkDiv)[(uint8_t)div_name] = 1UL << 30U; + } + else + { + ((volatile uint32_t *)pClkDiv)[(uint8_t)div_name] = (divided_by_value - 1U); + } + } +} + +/* Set RTC 1KHz Clock Divider */ +/** + * brief Setup rtc 1khz clock divider. + * param divided_by_value: Value to be divided + * return Nothing + */ +void CLOCK_SetRtc1khzClkDiv(uint32_t divided_by_value) +{ + PMC->RTCOSC32K = + (PMC->RTCOSC32K & ~PMC_RTCOSC32K_CLK1KHZDIV_MASK) | PMC_RTCOSC32K_CLK1KHZDIV(divided_by_value - 28U); +} + +/* Set RTC 1KHz Clock Divider */ +/** + * brief Setup rtc 1hz clock divider. + * param divided_by_value: Value to be divided + * return Nothing + */ +void CLOCK_SetRtc1hzClkDiv(uint32_t divided_by_value) +{ + if (divided_by_value == 0U) /*!< halt */ + { + PMC->RTCOSC32K |= (1UL << PMC_RTCOSC32K_CLK1HZDIVHALT_SHIFT); + } + else + { + PMC->RTCOSC32K = + (PMC->RTCOSC32K & ~PMC_RTCOSC32K_CLK1HZDIV_MASK) | PMC_RTCOSC32K_CLK1HZDIV(divided_by_value - 31744U); + } +} + +/* Set FRO Clocking */ +/** + * brief Initialize the Core clock to given frequency (12, 48 or 96 MHz). + * Turns on FRO and uses default CCO, if freq is 12000000, then high speed output is off, else high speed output is + * enabled. + * param iFreq : Desired frequency (must be one of #CLK_FRO_12MHZ or #CLK_FRO_48MHZ or #CLK_FRO_96MHZ) + * return returns success or fail status. + */ +status_t CLOCK_SetupFROClocking(uint32_t iFreq) +{ + if ((iFreq != 12000000U) && (iFreq != 96000000U)) + { + return kStatus_Fail; + } + /* Enable Analog Control module */ + SYSCON->PRESETCTRLCLR[2] = (1UL << SYSCON_PRESETCTRL2_ANALOG_CTRL_RST_SHIFT); + SYSCON->AHBCLKCTRLSET[2] = SYSCON_AHBCLKCTRL2_ANALOG_CTRL_MASK; + /* Power up the FRO192M */ + POWER_DisablePD(kPDRUNCFG_PD_FRO192M); + + if (iFreq == 96000000U) + { + ANACTRL->FRO192M_CTRL |= ANACTRL_FRO192M_CTRL_ENA_96MHZCLK(1); + } + /* always enable + else if (iFreq == 48000000U) + { + ANACTRL->FRO192M_CTRL |= ANACTRL_FRO192M_CTRL_ENA_48MHZCLK(1); + }*/ + else + { + ANACTRL->FRO192M_CTRL |= ANACTRL_FRO192M_CTRL_ENA_12MHZCLK(1); + } + return kStatus_Success; +} + +/* Set the FLASH wait states for the passed frequency */ +/** + * brief Set the flash wait states for the input freuqency. + * param iFreq: Input frequency + * return Nothing + */ +typedef struct +{ + uint32_t waitstate; + uint32_t freqMax; +} WaitStateInterval_t; + +/* clang-format off */ +/* Wait state if frequency is inferior to the one specified */ +static const WaitStateInterval_t IntervalList[] = { + {0, 11000000}, + {1, 22000000}, + {2, 33000000}, + {3, 44000000}, + {4, 55000000}, + {5, 66000000}, + {6, 84000000}, + {7, 104000000}, + {8, 119000000}, + {9, 129000000}, + {10, 144000000}, + {11, 150000000} /* Maximum allowed frequency (150 MHz) */ +}; +/* clang-format on */ + +void CLOCK_SetFLASHAccessCyclesForFreq(uint32_t system_freq_hz) +{ + /* Flash Controller & FMC internal number of Wait States (minus 1) */ + uint32_t num_wait_states = 15UL; /* Default to the maximum number of wait states */ + + for (size_t cnt = 0; cnt < (sizeof(IntervalList) / sizeof(WaitStateInterval_t)); cnt++) + { + if (system_freq_hz <= IntervalList[cnt].freqMax) + { + num_wait_states = IntervalList[cnt].waitstate; + break; + } + } + + FLASH->INT_CLR_STATUS = 0x1F; /* Clear all status flags */ + + FLASH->DATAW[0] = (FLASH->DATAW[0] & 0xFFFFFFF0UL) | + (num_wait_states & (SYSCON_FMCCR_FLASHTIM_MASK >> SYSCON_FMCCR_FLASHTIM_SHIFT)); + + FLASH->CMD = 0x2; /* CMD_SET_READ_MODE */ + + /* Wait until the cmd is completed (without error) */ + while ((FLASH->INT_STATUS & FLASH_INT_STATUS_DONE_MASK) == 0UL) + { + } + + /* Adjust FMC waiting time cycles (num_wait_states) */ + SYSCON->FMCCR = (SYSCON->FMCCR & ~SYSCON_FMCCR_FLASHTIM_MASK) | + ((num_wait_states << SYSCON_FMCCR_FLASHTIM_SHIFT) & SYSCON_FMCCR_FLASHTIM_MASK); +} + +/* Set EXT OSC Clk */ +/** + * brief Initialize the external osc clock to given frequency. + * Crystal oscillator with an operating frequency of 12 MHz to 32 MHz. + * Option for external clock input (bypass mode) for clock frequencies of up to 25 MHz. + * param iFreq : Desired frequency (must be equal to exact rate in Hz) + * return returns success or fail status. + */ +status_t CLOCK_SetupExtClocking(uint32_t iFreq) +{ + if (iFreq > 32000000U) + { + return kStatus_Fail; + } + /* Turn on power for crystal 32 MHz */ + POWER_DisablePD(kPDRUNCFG_PD_XTAL32M); + POWER_DisablePD(kPDRUNCFG_PD_LDOXO32M); + /* Enable clock_in clock for clock module. */ + SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK; + + s_Ext_Clk_Freq = iFreq; + return kStatus_Success; +} + +/* Set I2S MCLK Clk */ +/** + * brief Initialize the I2S MCLK clock to given frequency. + * param iFreq : Desired frequency (must be equal to exact rate in Hz) + * return returns success or fail status. + */ +status_t CLOCK_SetupI2SMClkClocking(uint32_t iFreq) +{ + s_I2S_Mclk_Freq = iFreq; + return kStatus_Success; +} + +/* Set PLU CLKIN Clk */ +/** + * brief Initialize the PLU CLKIN clock to given frequency. + * param iFreq : Desired frequency (must be equal to exact rate in Hz) + * return returns success or fail status. + */ +status_t CLOCK_SetupPLUClkInClocking(uint32_t iFreq) +{ + s_PLU_ClkIn_Freq = iFreq; + return kStatus_Success; +} + +/* Get CLOCK OUT Clk */ +/*! brief Return Frequency of ClockOut + * return Frequency of ClockOut + */ +uint32_t CLOCK_GetClockOutClkFreq(void) +{ + uint32_t freq = 0U; + + switch (SYSCON->CLKOUTSEL) + { + case 0U: + freq = CLOCK_GetCoreSysClkFreq(); + break; + + case 1U: + freq = CLOCK_GetPll0OutFreq(); + break; + + case 2U: + freq = CLOCK_GetExtClkFreq(); + break; + + case 3U: + freq = CLOCK_GetFroHfFreq(); + break; + + case 4U: + freq = CLOCK_GetFro1MFreq(); + break; + + case 5U: + freq = CLOCK_GetPll1OutFreq(); + break; + + case 6U: + freq = CLOCK_GetOsc32KFreq(); + break; + + case 7U: + freq = 0U; + break; + + default: + freq = 0U; + break; + } + return freq / ((SYSCON->CLKOUTDIV & 0xffU) + 1U); +} + +/* Get CAN Clk */ +/*! brief Return Frequency of Can Clock + * return Frequency of Can. + */ +uint32_t CLOCK_GetMCanClkFreq(void) +{ + uint32_t freq = 0U; + + switch (SYSCON->CANCLKSEL) + { + case 0U: + freq = CLOCK_GetCoreSysClkFreq() / ((SYSCON->CANCLKDIV & SYSCON_CANCLKDIV_DIV_MASK) + 1U); + break; + case 1U: + freq = CLOCK_GetFro1MFreq(); + break; + case 2U: + freq = CLOCK_GetOsc32KFreq(); + break; + case 7U: + freq = 0U; + break; + + default: + freq = 0U; + break; + } + + return freq; +} + +/* Get ADC Clk */ +/*! brief Return Frequency of Adc Clock + * return Frequency of Adc. + */ +uint32_t CLOCK_GetAdcClkFreq(void) +{ + uint32_t freq = 0U; + + switch (SYSCON->ADCCLKSEL) + { + case 0U: + freq = CLOCK_GetCoreSysClkFreq(); + break; + case 1U: + freq = CLOCK_GetPll0OutFreq(); + break; + case 2U: + freq = CLOCK_GetFroHfFreq(); + break; + case 7U: + freq = 0U; + break; + + default: + freq = 0U; + break; + } + + return freq / ((SYSCON->ADCCLKDIV & SYSCON_ADCCLKDIV_DIV_MASK) + 1U); +} + +/* Get USB0 Clk */ +/*! brief Return Frequency of Usb0 Clock + * return Frequency of Usb0 Clock. + */ +uint32_t CLOCK_GetUsb0ClkFreq(void) +{ + uint32_t freq = 0U; + + switch (SYSCON->USB0CLKSEL) + { + case 0U: + freq = CLOCK_GetCoreSysClkFreq(); + break; + case 1U: + freq = CLOCK_GetPll0OutFreq(); + break; + case 3U: + freq = CLOCK_GetFroHfFreq(); + break; + case 5U: + freq = CLOCK_GetPll1OutFreq(); + break; + case 7U: + freq = 0U; + break; + + default: + freq = 0U; + break; + } + + return freq / ((SYSCON->USB0CLKDIV & 0xffU) + 1U); +} + +/* Get USB1 Clk */ +/*! brief Return Frequency of Usb1 Clock + * return Frequency of Usb1 Clock. + */ +uint32_t CLOCK_GetUsb1ClkFreq(void) +{ + return ((ANACTRL->XO32M_CTRL & ANACTRL_XO32M_CTRL_ENABLE_PLL_USB_OUT_MASK) != 0UL) ? s_Ext_Clk_Freq : 0U; +} + +/* Get MCLK Clk */ +/*! brief Return Frequency of MClk Clock + * return Frequency of MClk Clock. + */ +uint32_t CLOCK_GetMclkClkFreq(void) +{ + uint32_t freq = 0U; + + switch (SYSCON->MCLKCLKSEL) + { + case 0U: + freq = CLOCK_GetFroHfFreq(); + break; + case 1U: + freq = CLOCK_GetPll0OutFreq(); + break; + case 7U: + freq = 0U; + break; + + default: + freq = 0U; + break; + } + + return freq / ((SYSCON->MCLKDIV & 0xffU) + 1U); +} + +/* Get SCTIMER Clk */ +/*! brief Return Frequency of SCTimer Clock + * return Frequency of SCTimer Clock. + */ +uint32_t CLOCK_GetSctClkFreq(void) +{ + uint32_t freq = 0U; + + switch (SYSCON->SCTCLKSEL) + { + case 0U: + freq = CLOCK_GetCoreSysClkFreq(); + break; + case 1U: + freq = CLOCK_GetPll0OutFreq(); + break; + case 2U: + freq = CLOCK_GetExtClkFreq(); + break; + case 3U: + freq = CLOCK_GetFroHfFreq(); + break; + case 5U: + freq = CLOCK_GetI2SMClkFreq(); + break; + case 7U: + freq = 0U; + break; + + default: + freq = 0U; + break; + } + + return freq / ((SYSCON->SCTCLKDIV & 0xffU) + 1U); +} + +/* Get FRO 12M Clk */ +/*! brief Return Frequency of FRO 12MHz + * return Frequency of FRO 12MHz + */ +uint32_t CLOCK_GetFro12MFreq(void) +{ + return ((ANACTRL->FRO192M_CTRL & ANACTRL_FRO192M_CTRL_ENA_12MHZCLK_MASK) != 0UL) ? 12000000U : 0U; +} + +/* Get FRO 1M Clk */ +/*! brief Return Frequency of FRO 1MHz + * return Frequency of FRO 1MHz + */ +uint32_t CLOCK_GetFro1MFreq(void) +{ + return ((SYSCON->CLOCK_CTRL & SYSCON_CLOCK_CTRL_FRO1MHZ_CLK_ENA_MASK) != 0UL) ? 1000000U : 0U; +} + +/* Get EXT OSC Clk */ +/*! brief Return Frequency of External Clock + * return Frequency of External Clock. If no external clock is used returns 0. + */ +uint32_t CLOCK_GetExtClkFreq(void) +{ + return ((ANACTRL->XO32M_CTRL & ANACTRL_XO32M_CTRL_ENABLE_SYSTEM_CLK_OUT_MASK) != 0UL) ? s_Ext_Clk_Freq : 0U; +} + +/* Get WATCH DOG Clk */ +/*! brief Return Frequency of Watchdog + * return Frequency of Watchdog + */ +uint32_t CLOCK_GetWdtClkFreq(void) +{ + return CLOCK_GetFro1MFreq() / ((SYSCON->WDTCLKDIV & SYSCON_WDTCLKDIV_DIV_MASK) + 1U); +} + +/* Get HF FRO Clk */ +/*! brief Return Frequency of High-Freq output of FRO + * return Frequency of High-Freq output of FRO + */ +uint32_t CLOCK_GetFroHfFreq(void) +{ + return ((ANACTRL->FRO192M_CTRL & ANACTRL_FRO192M_CTRL_ENA_96MHZCLK_MASK) != 0UL) ? 96000000U : 0U; +} + +/* Get SYSTEM PLL Clk */ +/*! brief Return Frequency of PLL + * return Frequency of PLL + */ +uint32_t CLOCK_GetPll0OutFreq(void) +{ + return s_Pll0_Freq; +} + +/* Get USB PLL Clk */ +/*! brief Return Frequency of USB PLL + * return Frequency of PLL + */ +uint32_t CLOCK_GetPll1OutFreq(void) +{ + return s_Pll1_Freq; +} + +/* Get RTC OSC Clk */ +/*! brief Return Frequency of 32kHz osc + * return Frequency of 32kHz osc + */ +uint32_t CLOCK_GetOsc32KFreq(void) +{ + return ((0UL == (PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_FRO32K_MASK)) && + (0UL == (PMC->RTCOSC32K & PMC_RTCOSC32K_SEL_MASK))) ? + CLK_RTC_32K_CLK : + ((0UL == (PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_XTAL32K_MASK)) && + ((PMC->RTCOSC32K & PMC_RTCOSC32K_SEL_MASK) != 0UL)) ? + CLK_RTC_32K_CLK : + 0UL; +} + +/* Get MAIN Clk */ +/*! brief Return Frequency of Core System + * return Frequency of Core System + */ +uint32_t CLOCK_GetCoreSysClkFreq(void) +{ + uint32_t freq = 0U; + + switch (SYSCON->MAINCLKSELB) + { + case 0U: + if (SYSCON->MAINCLKSELA == 0U) + { + freq = CLOCK_GetFro12MFreq(); + } + else if (SYSCON->MAINCLKSELA == 1U) + { + freq = CLOCK_GetExtClkFreq(); + } + else if (SYSCON->MAINCLKSELA == 2U) + { + freq = CLOCK_GetFro1MFreq(); + } + else if (SYSCON->MAINCLKSELA == 3U) + { + freq = CLOCK_GetFroHfFreq(); + } + else + { + /* Added comments to avoid the violation of MISRA C-2012 rule 15.7 */ + } + break; + case 1U: + freq = CLOCK_GetPll0OutFreq(); + break; + case 2U: + freq = CLOCK_GetPll1OutFreq(); + break; + + case 3U: + freq = CLOCK_GetOsc32KFreq(); + break; + + default: + freq = 0U; + break; + } + + return freq; +} + +/* Get I2S MCLK Clk */ +/*! brief Return Frequency of I2S MCLK Clock + * return Frequency of I2S MCLK Clock + */ +uint32_t CLOCK_GetI2SMClkFreq(void) +{ + return s_I2S_Mclk_Freq; +} + +/* Get PLU CLKIN Clk */ +/*! brief Return Frequency of PLU CLKIN Clock + * return Frequency of PLU CLKIN Clock + */ +uint32_t CLOCK_GetPLUClkInFreq(void) +{ + return s_PLU_ClkIn_Freq; +} + +/* Get FLEXCOMM input clock */ +/*! brief Return Frequency of flexcomm input clock + * param id : flexcomm instance id + * return Frequency value + */ +uint32_t CLOCK_GetFlexCommInputClock(uint32_t id) +{ + uint32_t freq = 0U; + + switch (SYSCON->FCCLKSELX[id]) + { + case 0U: + freq = CLOCK_GetCoreSysClkFreq(); + break; + case 1U: + freq = CLOCK_GetPll0OutFreq() / ((SYSCON->PLL0CLKDIV & 0xffU) + 1U); + break; + case 2U: + freq = CLOCK_GetFro12MFreq(); + break; + case 3U: + freq = CLOCK_GetFroHfFreq() / ((SYSCON->FROHFDIV & 0xffU) + 1U); + break; + case 4U: + freq = CLOCK_GetFro1MFreq(); + break; + case 5U: + freq = CLOCK_GetI2SMClkFreq(); + break; + case 6U: + freq = CLOCK_GetOsc32KFreq(); + break; + case 7U: + freq = 0U; + break; + + default: + freq = 0U; + break; + } + + return freq; +} + +/* Get FLEXCOMM Clk */ +uint32_t CLOCK_GetFlexCommClkFreq(uint32_t id) +{ + uint32_t freq = 0U; + + freq = CLOCK_GetFlexCommInputClock(id); + return freq / (1UL + (SYSCON->FLEXFRGXCTRL[id] & SYSCON_FLEXFRG0CTRL_MULT_MASK) / + ((SYSCON->FLEXFRGXCTRL[id] & SYSCON_FLEXFRG0CTRL_DIV_MASK) + 1UL)); +} + +/* Get HS_LPSI Clk */ +uint32_t CLOCK_GetHsLspiClkFreq(void) +{ + uint32_t freq = 0U; + + switch (SYSCON->HSLSPICLKSEL) + { + case 0U: + freq = CLOCK_GetCoreSysClkFreq(); + break; + case 1U: + freq = CLOCK_GetPll0OutFreq() / ((SYSCON->PLL0CLKDIV & 0xffU) + 1U); + break; + case 2U: + freq = CLOCK_GetFro12MFreq(); + break; + case 3U: + freq = CLOCK_GetFroHfFreq() / ((SYSCON->FROHFDIV & 0xffU) + 1U); + break; + case 4U: + freq = CLOCK_GetFro1MFreq(); + break; + case 6U: + freq = CLOCK_GetOsc32KFreq(); + break; + case 7U: + freq = 0U; + break; + + default: + freq = 0U; + break; + } + + return freq; +} + +/* Get CTimer Clk */ +/*! brief Return Frequency of CTimer functional Clock + * return Frequency of CTimer functional Clock + */ +uint32_t CLOCK_GetCTimerClkFreq(uint32_t id) +{ + uint32_t freq = 0U; + + switch (SYSCON->CTIMERCLKSELX[id]) + { + case 0U: + freq = CLOCK_GetCoreSysClkFreq(); + break; + case 1U: + freq = CLOCK_GetPll0OutFreq(); + break; + case 3U: + freq = CLOCK_GetFroHfFreq(); + break; + case 4U: + freq = CLOCK_GetFro1MFreq(); + break; + case 5U: + freq = CLOCK_GetI2SMClkFreq(); + break; + case 6U: + freq = CLOCK_GetOsc32KFreq(); + break; + case 7U: + freq = 0U; + break; + + default: + freq = 0U; + break; + } + + return freq; +} + +/* Get Systick Clk */ +/*! brief Return Frequency of SystickClock + * return Frequency of Systick Clock + */ +uint32_t CLOCK_GetSystickClkFreq(uint32_t id) +{ + uint32_t freq = 0U; + + switch (SYSCON->SYSTICKCLKSELX[id]) + { + case 0U: + /*Niobe4mini just has one SYSTICKSEL and SYSTICKDIV register, Fix coverity problem in this way temporarily + */ + freq = CLOCK_GetCoreSysClkFreq() / (((SYSCON->SYSTICKCLKDIV0) & 0xffU) + 1U); + break; + case 1U: + freq = CLOCK_GetFro1MFreq(); + break; + case 2U: + freq = CLOCK_GetOsc32KFreq(); + break; + case 7U: + freq = 0U; + break; + + default: + freq = 0U; + break; + } + + return freq; +} + +/* Set FlexComm Clock */ +/** + * brief Set the flexcomm output frequency. + * param id : flexcomm instance id + * freq : output frequency + * return 0 : the frequency range is out of range. + * 1 : switch successfully. + */ +uint32_t CLOCK_SetFlexCommClock(uint32_t id, uint32_t freq) +{ + uint32_t input = CLOCK_GetFlexCommClkFreq(id); + uint32_t mul; + + if ((freq > 48000000UL) || (freq > input) || (input / freq >= 2UL)) + { + /* FRG output frequency should be less than equal to 48MHz */ + return 0UL; + } + else + { + mul = (uint32_t)((((uint64_t)input - freq) * 256ULL) / ((uint64_t)freq)); + SYSCON->FLEXFRGXCTRL[id] = (mul << 8U) | 0xFFU; + return 1UL; + } +} + +/* Get IP Clk */ +/*! brief Return Frequency of selected clock + * return Frequency of selected clock + */ +uint32_t CLOCK_GetFreq(clock_name_t clockName) +{ + uint32_t freq; + switch (clockName) + { + case kCLOCK_CoreSysClk: + freq = CLOCK_GetCoreSysClkFreq(); + break; + case kCLOCK_BusClk: + freq = CLOCK_GetCoreSysClkFreq() / ((SYSCON->AHBCLKDIV & 0xffU) + 1U); + break; + case kCLOCK_ClockOut: + freq = CLOCK_GetClockOutClkFreq(); + break; + case kCLOCK_Pll1Out: + freq = CLOCK_GetPll1OutFreq(); + break; + case kCLOCK_Mclk: + freq = CLOCK_GetMclkClkFreq(); + break; + case kCLOCK_FroHf: + freq = CLOCK_GetFroHfFreq(); + break; + case kCLOCK_Fro12M: + freq = CLOCK_GetFro12MFreq(); + break; + case kCLOCK_Fro1M: + freq = CLOCK_GetFro1MFreq(); + break; + case kCLOCK_ExtClk: + freq = CLOCK_GetExtClkFreq(); + break; + case kCLOCK_Pll0Out: + freq = CLOCK_GetPll0OutFreq(); + break; + case kCLOCK_FlexI2S: + freq = CLOCK_GetI2SMClkFreq(); + break; + default: + freq = 0U; + break; + } + return freq; +} + +/* Find SELP, SELI, and SELR values for raw M value, max M = MVALMAX */ +static void pllFindSel(uint32_t M, uint32_t *pSelP, uint32_t *pSelI, uint32_t *pSelR) +{ + uint32_t seli, selp; + /* bandwidth: compute selP from Multiplier */ + if ((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_MDIV_EXT_MASK) != 0UL) /* normal mode */ + { + selp = (M >> 2U) + 1U; + if (selp >= 31U) + { + selp = 31U; + } + *pSelP = selp; + + if (M >= 8000UL) + { + seli = 1UL; + } + else if (M >= 122UL) + { + seli = (uint32_t)(8000UL / M); /*floor(8000/M) */ + } + else + { + seli = 2UL * ((uint32_t)(M / 4UL)) + 3UL; /* 2*floor(M/4) + 3 */ + } + + if (seli >= 63UL) + { + seli = 63UL; + } + *pSelI = seli; + + *pSelR = 0UL; + } + else + { + /* Note: If the spread spectrum mode, choose N to ensure 3 MHz < Fin/N < 5 MHz */ + *pSelP = 3U; + *pSelI = 4U; + *pSelR = 4U; + } +} + +/* Get predivider (N) from PLL0 NDEC setting */ +static uint32_t findPll0PreDiv(void) +{ + uint32_t preDiv = 1UL; + + /* Direct input is not used? */ + if ((SYSCON->PLL0CTRL & SYSCON_PLL0CTRL_BYPASSPREDIV_MASK) == 0UL) + { + preDiv = SYSCON->PLL0NDEC & SYSCON_PLL0NDEC_NDIV_MASK; + if (preDiv == 0UL) + { + preDiv = 1UL; + } + } + return preDiv; +} + +/* Get predivider (N) from PLL1 NDEC setting */ +static uint32_t findPll1PreDiv(void) +{ + uint32_t preDiv = 1UL; + + /* Direct input is not used? */ + if ((SYSCON->PLL1CTRL & SYSCON_PLL1CTRL_BYPASSPREDIV_MASK) == 0UL) + { + preDiv = SYSCON->PLL1NDEC & SYSCON_PLL1NDEC_NDIV_MASK; + if (preDiv == 0UL) + { + preDiv = 1UL; + } + } + return preDiv; +} + +/* Get postdivider (P) from PLL0 PDEC setting */ +static uint32_t findPll0PostDiv(void) +{ + uint32_t postDiv = 1UL; + + if ((SYSCON->PLL0CTRL & SYSCON_PLL0CTRL_BYPASSPOSTDIV_MASK) == 0UL) + { + if ((SYSCON->PLL0CTRL & SYSCON_PLL0CTRL_BYPASSPOSTDIV2_MASK) != 0UL) + { + postDiv = SYSCON->PLL0PDEC & SYSCON_PLL0PDEC_PDIV_MASK; + } + else + { + postDiv = 2UL * (SYSCON->PLL0PDEC & SYSCON_PLL0PDEC_PDIV_MASK); + } + if (postDiv == 0UL) + { + postDiv = 2UL; + } + } + return postDiv; +} + +/* Get postdivider (P) from PLL1 PDEC setting. */ +static uint32_t findPll1PostDiv(void) +{ + uint32_t postDiv = 1UL; + + if ((SYSCON->PLL1CTRL & SYSCON_PLL1CTRL_BYPASSPOSTDIV_MASK) == 0UL) + { + if ((SYSCON->PLL1CTRL & SYSCON_PLL1CTRL_BYPASSPOSTDIV2_MASK) != 0UL) + { + postDiv = SYSCON->PLL1PDEC & SYSCON_PLL1PDEC_PDIV_MASK; + } + else + { + postDiv = 2UL * (SYSCON->PLL1PDEC & SYSCON_PLL1PDEC_PDIV_MASK); + } + if (postDiv == 0UL) + { + postDiv = 2UL; + } + } + + return postDiv; +} + +/* Get multiplier (M) from PLL0 SSCG and SEL_EXT settings */ +static float findPll0MMult(void) +{ + float mMult = 1.0F; + float mMult_fract; + uint32_t mMult_int; + + if ((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_SEL_EXT_MASK) != 0UL) + { + mMult = + (float)(uint32_t)((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_MDIV_EXT_MASK) >> SYSCON_PLL0SSCG1_MDIV_EXT_SHIFT); + } + else + { + mMult_int = ((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_MD_MBS_MASK) << 7U); + mMult_int = mMult_int | ((SYSCON->PLL0SSCG0) >> PLL0_SSCG_MD_INT_P); + mMult_fract = ((float)(uint32_t)((SYSCON->PLL0SSCG0) & PLL0_SSCG_MD_FRACT_M) / + (float)(uint32_t)(1UL << PLL0_SSCG_MD_INT_P)); + mMult = (float)mMult_int + mMult_fract; + } + if (mMult == 0.0F) + { + mMult = 1.0F; + } + return mMult; +} + +/* Get multiplier (M) from PLL1 MDEC. */ +static uint32_t findPll1MMult(void) +{ + uint32_t mMult = 1UL; + + mMult = SYSCON->PLL1MDEC & SYSCON_PLL1MDEC_MDIV_MASK; + + if (mMult == 0UL) + { + mMult = 1UL; + } + + return mMult; +} + +/* Find greatest common divisor between m and n */ +static uint32_t FindGreatestCommonDivisor(uint32_t m, uint32_t n) +{ + uint32_t tmp; + + while (n != 0U) + { + tmp = n; + n = m % n; + m = tmp; + } + + return m; +} + +/* + * Set PLL0 output based on desired output rate. + * In this function, the it calculates the PLL0 setting for output frequency from input clock + * frequency. The calculation would cost a few time. So it is not recommaned to use it frequently. + * the "pllctrl", "pllndec", "pllpdec", "pllmdec" would updated in this function. + */ +static pll_error_t CLOCK_GetPll0ConfigInternal(uint32_t finHz, uint32_t foutHz, pll_setup_t *pSetup, bool useSS) +{ + uint32_t nDivOutHz, fccoHz; + uint32_t pllPreDivider, pllMultiplier, pllPostDivider; + uint32_t pllDirectInput, pllDirectOutput; + uint32_t pllSelP, pllSelI, pllSelR, uplimoff; + + /* Baseline parameters (no input or output dividers) */ + pllPreDivider = 1U; /* 1 implies pre-divider will be disabled */ + pllPostDivider = 1U; /* 1 implies post-divider will be disabled */ + pllDirectOutput = 1U; + + /* Verify output rate parameter */ + if (foutHz > PLL_MAX_CCO_FREQ_MHZ) + { + /* Maximum PLL output with post divider=1 cannot go above this frequency */ + return kStatus_PLL_OutputTooHigh; + } + if (foutHz < (PLL_MIN_CCO_FREQ_MHZ / (PVALMAX << 1U))) + { + /* Minmum PLL output with maximum post divider cannot go below this frequency */ + return kStatus_PLL_OutputTooLow; + } + + /* If using SS mode, input clock needs to be between 3MHz and 20MHz */ + if (useSS) + { + /* Verify input rate parameter */ + if (finHz < PLL_MIN_IN_SSMODE) + { + /* Input clock into the PLL cannot be lower than this */ + return kStatus_PLL_InputTooLow; + } + /* PLL input in SS mode must be under 20MHz */ + if (finHz > (PLL_MAX_IN_SSMODE * NVALMAX)) + { + return kStatus_PLL_InputTooHigh; + } + } + else + { + /* Verify input rate parameter */ + if (finHz < PLL_LOWER_IN_LIMIT) + { + /* Input clock into the PLL cannot be lower than this */ + return kStatus_PLL_InputTooLow; + } + if (finHz > PLL_HIGHER_IN_LIMIT) + { + /* Input clock into the PLL cannot be higher than this */ + return kStatus_PLL_InputTooHigh; + } + } + + /* Find the optimal CCO frequency for the output and input that + will keep it inside the PLL CCO range. This may require + tweaking the post-divider for the PLL. */ + fccoHz = foutHz; + while (fccoHz < PLL_MIN_CCO_FREQ_MHZ) + { + /* CCO output is less than minimum CCO range, so the CCO output + needs to be bumped up and the post-divider is used to bring + the PLL output back down. */ + pllPostDivider++; + if (pllPostDivider > PVALMAX) + { + return kStatus_PLL_OutsideIntLimit; + } + + /* Target CCO goes up, PLL output goes down */ + /* divide-by-2 divider in the post-divider is always work*/ + fccoHz = foutHz * (pllPostDivider * 2U); + pllDirectOutput = 0U; + } + + /* Determine if a pre-divider is needed to get the best frequency */ + if ((finHz > PLL_LOWER_IN_LIMIT) && (fccoHz >= finHz) && (useSS == false)) + { + uint32_t a = FindGreatestCommonDivisor(fccoHz, finHz); + + if (a > PLL_LOWER_IN_LIMIT) + { + a = finHz / a; + if ((a != 0U) && (a < PLL_MAX_N_DIV)) + { + pllPreDivider = a; + } + } + } + + /* Bypass pre-divider hardware if pre-divider is 1 */ + if (pllPreDivider > 1U) + { + pllDirectInput = 0U; + } + else + { + pllDirectInput = 1U; + } + + /* Determine PLL multipler */ + nDivOutHz = (finHz / pllPreDivider); + pllMultiplier = (fccoHz / nDivOutHz); + + /* Find optimal values for filter */ + if (useSS == false) + { + /* Will bumping up M by 1 get us closer to the desired CCO frequency? */ + if ((nDivOutHz * ((pllMultiplier * 2U) + 1U)) < (fccoHz * 2U)) + { + pllMultiplier++; + } + + /* Setup filtering */ + pllFindSel(pllMultiplier, &pllSelP, &pllSelI, &pllSelR); + uplimoff = 0U; + + /* Get encoded value for M (mult) and use manual filter, disable SS mode */ + pSetup->pllsscg[1] = + (uint32_t)((PLL_SSCG1_MDEC_VAL_SET(pllMultiplier)) | (1UL << SYSCON_PLL0SSCG1_SEL_EXT_SHIFT)); + } + else + { + uint64_t fc; + + /* Filtering will be handled by SSC */ + pllSelR = 0UL; + pllSelI = 0UL; + pllSelP = 0UL; + uplimoff = 1U; + + /* The PLL multiplier will get very close and slightly under the + desired target frequency. A small fractional component can be + added to fine tune the frequency upwards to the target. */ + fc = ((uint64_t)(uint32_t)(fccoHz % nDivOutHz) << 25UL) / nDivOutHz; + + /* Set multiplier */ + pSetup->pllsscg[0] = (uint32_t)(PLL0_SSCG_MD_INT_SET(pllMultiplier) | PLL0_SSCG_MD_FRACT_SET((uint32_t)fc)); + pSetup->pllsscg[1] = (uint32_t)(PLL0_SSCG_MD_INT_SET(pllMultiplier) >> 32U); + } + + /* Get encoded values for N (prediv) and P (postdiv) */ + pSetup->pllndec = PLL_NDEC_VAL_SET(pllPreDivider); + pSetup->pllpdec = PLL_PDEC_VAL_SET(pllPostDivider); + + /* PLL control */ + pSetup->pllctrl = (pllSelR << SYSCON_PLL0CTRL_SELR_SHIFT) | /* Filter coefficient */ + (pllSelI << SYSCON_PLL0CTRL_SELI_SHIFT) | /* Filter coefficient */ + (pllSelP << SYSCON_PLL0CTRL_SELP_SHIFT) | /* Filter coefficient */ + (0UL << SYSCON_PLL0CTRL_BYPASSPLL_SHIFT) | /* PLL bypass mode disabled */ + (uplimoff << SYSCON_PLL0CTRL_LIMUPOFF_SHIFT) | /* SS/fractional mode disabled */ + (pllDirectInput << SYSCON_PLL0CTRL_BYPASSPREDIV_SHIFT) | /* Bypass pre-divider? */ + (pllDirectOutput << SYSCON_PLL0CTRL_BYPASSPOSTDIV_SHIFT) | /* Bypass post-divider? */ + (1UL << SYSCON_PLL0CTRL_CLKEN_SHIFT); /* Ensure the PLL clock output */ + + return kStatus_PLL_Success; +} + +#if (defined(CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) && CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) +/* Alloct the static buffer for cache. */ +static pll_setup_t s_PllSetupCacheStruct[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT]; +static uint32_t s_FinHzCache[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT] = {0}; +static uint32_t s_FoutHzCache[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT] = {0}; +static bool s_UseSSCache[CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT] = {false}; +static uint32_t s_PllSetupCacheIdx = 0U; +#endif /* CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT */ + +/* + * Calculate the PLL setting values from input clock freq to output freq. + */ +static pll_error_t CLOCK_GetPll0Config(uint32_t finHz, uint32_t foutHz, pll_setup_t *pSetup, bool useSS) +{ + pll_error_t retErr; +#if (defined(CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) && CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) + uint32_t i; + + for (i = 0U; i < CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT; i++) + { + if ((finHz == s_FinHzCache[i]) && (foutHz == s_FoutHzCache[i]) && (useSS == s_UseSSCache[i])) + { + /* Hit the target in cache buffer. */ + pSetup->pllctrl = s_PllSetupCacheStruct[i].pllctrl; + pSetup->pllndec = s_PllSetupCacheStruct[i].pllndec; + pSetup->pllpdec = s_PllSetupCacheStruct[i].pllpdec; + pSetup->pllsscg[0] = s_PllSetupCacheStruct[i].pllsscg[0]; + pSetup->pllsscg[1] = s_PllSetupCacheStruct[i].pllsscg[1]; + retErr = kStatus_PLL_Success; + break; + } + } + + if (i < CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) + { + return retErr; + } +#endif /* CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT */ + + retErr = CLOCK_GetPll0ConfigInternal(finHz, foutHz, pSetup, useSS); + +#if (defined(CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) && CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT) + /* Cache the most recent calulation result into buffer. */ + s_FinHzCache[s_PllSetupCacheIdx] = finHz; + s_FoutHzCache[s_PllSetupCacheIdx] = foutHz; + s_UseSSCache[s_PllSetupCacheIdx] = useSS; + + s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllctrl = pSetup->pllctrl; + s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllndec = pSetup->pllndec; + s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllpdec = pSetup->pllpdec; + s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllsscg[0] = pSetup->pllsscg[0]; + s_PllSetupCacheStruct[s_PllSetupCacheIdx].pllsscg[1] = pSetup->pllsscg[1]; + /* Update the index for next available buffer. */ + s_PllSetupCacheIdx = (s_PllSetupCacheIdx + 1U) % CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT; +#endif /* CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT */ + + return retErr; +} + +/* Update local PLL rate variable */ +static void CLOCK_GetPLL0OutFromSetupUpdate(pll_setup_t *pSetup) +{ + s_Pll0_Freq = CLOCK_GetPLL0OutFromSetup(pSetup); +} + +/* Update local PLL1 rate variable */ +static void CLOCK_GetPLL1OutFromSetupUpdate(pll_setup_t *pSetup) +{ + s_Pll1_Freq = CLOCK_GetPLL1OutFromSetup(pSetup); +} + +/* Return System PLL input clock rate */ +/*! brief Return PLL0 input clock rate + * return PLL0 input clock rate + */ +uint32_t CLOCK_GetPLL0InClockRate(void) +{ + uint32_t clkRate = 0U; + + switch ((SYSCON->PLL0CLKSEL & SYSCON_PLL0CLKSEL_SEL_MASK)) + { + case 0x00U: + clkRate = CLK_FRO_12MHZ; + break; + + case 0x01U: + clkRate = CLOCK_GetExtClkFreq(); + break; + + case 0x02U: + clkRate = CLOCK_GetFro1MFreq(); + break; + + case 0x03U: + clkRate = CLOCK_GetOsc32KFreq(); + break; + + default: + clkRate = 0U; + break; + } + + return clkRate; +} + +/* Return PLL1 input clock rate */ +uint32_t CLOCK_GetPLL1InClockRate(void) +{ + uint32_t clkRate = 0U; + + switch ((SYSCON->PLL1CLKSEL & SYSCON_PLL1CLKSEL_SEL_MASK)) + { + case 0x00U: + clkRate = CLK_FRO_12MHZ; + break; + + case 0x01U: + clkRate = CLOCK_GetExtClkFreq(); + break; + + case 0x02U: + clkRate = CLOCK_GetFro1MFreq(); + break; + + case 0x03U: + clkRate = CLOCK_GetOsc32KFreq(); + break; + + default: + clkRate = 0U; + break; + } + + return clkRate; +} + +/* Return PLL0 output clock rate from setup structure */ +/*! brief Return PLL0 output clock rate from setup structure + * param pSetup : Pointer to a PLL setup structure + * return PLL0 output clock rate the setup structure will generate + */ +uint32_t CLOCK_GetPLL0OutFromSetup(pll_setup_t *pSetup) +{ + uint32_t clkRate = 0; + uint32_t prediv, postdiv; + float workRate = 0.0F; + + /* Get the input clock frequency of PLL. */ + clkRate = CLOCK_GetPLL0InClockRate(); + + if (((pSetup->pllctrl & SYSCON_PLL0CTRL_BYPASSPLL_MASK) == 0UL) && + ((pSetup->pllctrl & SYSCON_PLL0CTRL_CLKEN_MASK) != 0UL) && + ((PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_PLL0_MASK) == 0UL) && + ((PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_PLL0_SSCG_MASK) == 0UL)) + { + prediv = findPll0PreDiv(); + postdiv = findPll0PostDiv(); + /* Adjust input clock */ + clkRate = clkRate / prediv; + /* MDEC used for rate */ + workRate = (float)clkRate * (float)findPll0MMult(); + workRate /= (float)postdiv; + } + + return (uint32_t)workRate; +} + +/* Return PLL1 output clock rate from setup structure */ +/*! brief Return PLL1 output clock rate from setup structure + * param pSetup : Pointer to a PLL setup structure + * return PLL0 output clock rate the setup structure will generate + */ +uint32_t CLOCK_GetPLL1OutFromSetup(pll_setup_t *pSetup) +{ + uint32_t clkRate = 0; + uint32_t prediv, postdiv; + uint32_t workRate = 0UL; + + /* Get the input clock frequency of PLL. */ + clkRate = CLOCK_GetPLL1InClockRate(); + + if (((pSetup->pllctrl & SYSCON_PLL1CTRL_BYPASSPLL_MASK) == 0UL) && + ((pSetup->pllctrl & SYSCON_PLL1CTRL_CLKEN_MASK) != 0UL) && + ((PMC->PDRUNCFG0 & PMC_PDRUNCFG0_PDEN_PLL1_MASK) == 0UL)) + { + prediv = findPll1PreDiv(); + postdiv = findPll1PostDiv(); + /* Adjust input clock */ + clkRate = clkRate / prediv; + /* MDEC used for rate */ + workRate = clkRate * findPll1MMult(); + workRate /= postdiv; + } + + return workRate; +} + +/* Set the current PLL0 Rate */ +/*! brief Store the current PLL rate + * param rate: Current rate of the PLL + * return Nothing + **/ +void CLOCK_SetStoredPLL0ClockRate(uint32_t rate) +{ + s_Pll0_Freq = rate; +} + +/* Return PLL0 output clock rate */ +/*! brief Return PLL0 output clock rate + * param recompute : Forces a PLL rate recomputation if true + * return PLL0 output clock rate + * note The PLL rate is cached in the driver in a variable as + * the rate computation function can take some time to perform. It + * is recommended to use 'false' with the 'recompute' parameter. + */ +uint32_t CLOCK_GetPLL0OutClockRate(bool recompute) +{ + pll_setup_t Setup; + uint32_t rate; + + if ((recompute) || (s_Pll0_Freq == 0U)) + { + Setup.pllctrl = SYSCON->PLL0CTRL; + Setup.pllndec = SYSCON->PLL0NDEC; + Setup.pllpdec = SYSCON->PLL0PDEC; + Setup.pllsscg[0] = SYSCON->PLL0SSCG0; + Setup.pllsscg[1] = SYSCON->PLL0SSCG1; + + CLOCK_GetPLL0OutFromSetupUpdate(&Setup); + } + + rate = s_Pll0_Freq; + + return rate; +} + +/*! brief Return PLL1 output clock rate + * param recompute : Forces a PLL rate recomputation if true + * return PLL1 output clock rate + * note The PLL rate is cached in the driver in a variable as + * the rate computation function can take some time to perform. It + * is recommended to use 'false' with the 'recompute' parameter. + */ +uint32_t CLOCK_GetPLL1OutClockRate(bool recompute) +{ + pll_setup_t Setup; + uint32_t rate; + + if ((recompute) || (s_Pll1_Freq == 0U)) + { + Setup.pllctrl = SYSCON->PLL1CTRL; + Setup.pllndec = SYSCON->PLL1NDEC; + Setup.pllpdec = SYSCON->PLL1PDEC; + Setup.pllmdec = SYSCON->PLL1MDEC; + CLOCK_GetPLL1OutFromSetupUpdate(&Setup); + } + + rate = s_Pll1_Freq; + + return rate; +} + +/* Set PLL0 output based on the passed PLL setup data */ +/*! brief Set PLL output based on the passed PLL setup data + * param pControl : Pointer to populated PLL control structure to generate setup with + * param pSetup : Pointer to PLL setup structure to be filled + * return PLL_ERROR_SUCCESS on success, or PLL setup error code + * note Actual frequency for setup may vary from the desired frequency based on the + * accuracy of input clocks, rounding, non-fractional PLL mode, etc. + */ +pll_error_t CLOCK_SetupPLL0Data(pll_config_t *pControl, pll_setup_t *pSetup) +{ + uint32_t inRate; + bool useSS = (bool)((pControl->flags & PLL_CONFIGFLAG_FORCENOFRACT) == 0UL); + + pll_error_t pllError; + + /* Determine input rate for the PLL */ + if ((pControl->flags & PLL_CONFIGFLAG_USEINRATE) != 0UL) + { + inRate = pControl->inputRate; + } + else + { + inRate = CLOCK_GetPLL0InClockRate(); + } + + /* PLL flag options */ + pllError = CLOCK_GetPll0Config(inRate, pControl->desiredRate, pSetup, useSS); + if ((useSS) && (pllError == kStatus_PLL_Success)) + { + /* If using SS mode, then some tweaks are made to the generated setup */ + pSetup->pllsscg[1] |= (uint32_t)pControl->ss_mf | (uint32_t)pControl->ss_mr | (uint32_t)pControl->ss_mc; + if (pControl->mfDither) + { + pSetup->pllsscg[1] |= (1UL << SYSCON_PLL0SSCG1_DITHER_SHIFT); + } + } + + return pllError; +} + +/* Set PLL0 output from PLL setup structure */ +/*! brief Set PLL output from PLL setup structure (precise frequency) + * param pSetup : Pointer to populated PLL setup structure + * param flagcfg : Flag configuration for PLL config structure + * return PLL_ERROR_SUCCESS on success, or PLL setup error code + * note This function will power off the PLL, setup the PLL with the + * new setup data, and then optionally powerup the PLL, wait for PLL lock, + * and adjust system voltages to the new PLL rate. The function will not + * alter any source clocks (ie, main systen clock) that may use the PLL, + * so these should be setup prior to and after exiting the function. + */ +pll_error_t CLOCK_SetupPLL0Prec(pll_setup_t *pSetup, uint32_t flagcfg) +{ + uint32_t inRate, clkRate, prediv; + + /* Power off PLL during setup changes */ + POWER_EnablePD(kPDRUNCFG_PD_PLL0); + POWER_EnablePD(kPDRUNCFG_PD_PLL0_SSCG); + + pSetup->flags = flagcfg; + + /* Write PLL setup data */ + SYSCON->PLL0CTRL = pSetup->pllctrl; + SYSCON->PLL0NDEC = pSetup->pllndec; + SYSCON->PLL0NDEC = pSetup->pllndec | (1UL << SYSCON_PLL0NDEC_NREQ_SHIFT); /* latch */ + SYSCON->PLL0PDEC = pSetup->pllpdec; + SYSCON->PLL0PDEC = pSetup->pllpdec | (1UL << SYSCON_PLL0PDEC_PREQ_SHIFT); /* latch */ + SYSCON->PLL0SSCG0 = pSetup->pllsscg[0]; + SYSCON->PLL0SSCG1 = pSetup->pllsscg[1]; + SYSCON->PLL0SSCG1 = + pSetup->pllsscg[1] | (1UL << SYSCON_PLL0SSCG1_MREQ_SHIFT) | (1UL << SYSCON_PLL0SSCG1_MD_REQ_SHIFT); /* latch */ + + POWER_DisablePD(kPDRUNCFG_PD_PLL0); + POWER_DisablePD(kPDRUNCFG_PD_PLL0_SSCG); + + if ((pSetup->flags & PLL_SETUPFLAG_WAITLOCK) != 0UL) + { + if ((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_MDIV_EXT_MASK) != 0UL) /* normal mode */ + { + inRate = CLOCK_GetPLL0InClockRate(); + prediv = findPll0PreDiv(); + /* Adjust input clock */ + clkRate = inRate / prediv; + /* The lock signal is only reliable between fref[2] :100 kHz to 20 MHz. */ + if ((clkRate >= 100000UL) && (clkRate <= 20000000UL)) + { + while (CLOCK_IsPLL0Locked() == false) + { + } + } + else + { + SDK_DelayAtLeastUs(6000U, + SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); /* software should use a 6 ms time interval + to insure the PLL will be stable */ + } + } + else /* spread spectrum mode */ + { + SDK_DelayAtLeastUs(6000U, + SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); /* software should use a 6 ms time interval to + insure the PLL will be stable */ + } + } + + /* Update current programmed PLL rate var */ + CLOCK_GetPLL0OutFromSetupUpdate(pSetup); + + /* System voltage adjustment, occurs prior to setting main system clock */ + if ((pSetup->flags & PLL_SETUPFLAG_ADGVOLT) != 0UL) + { + POWER_SetVoltageForFreq(s_Pll0_Freq); + } + + return kStatus_PLL_Success; +} + +/* Setup PLL Frequency from pre-calculated value */ +/** + * brief Set PLL0 output from PLL setup structure (precise frequency) + * param pSetup : Pointer to populated PLL setup structure + * return kStatus_PLL_Success on success, or PLL setup error code + * note This function will power off the PLL, setup the PLL with the + * new setup data, and then optionally powerup the PLL, wait for PLL lock, + * and adjust system voltages to the new PLL rate. The function will not + * alter any source clocks (ie, main systen clock) that may use the PLL, + * so these should be setup prior to and after exiting the function. + */ +pll_error_t CLOCK_SetPLL0Freq(const pll_setup_t *pSetup) +{ + uint32_t inRate, clkRate, prediv; + /* Power off PLL during setup changes */ + POWER_EnablePD(kPDRUNCFG_PD_PLL0); + POWER_EnablePD(kPDRUNCFG_PD_PLL0_SSCG); + + /* Write PLL setup data */ + SYSCON->PLL0CTRL = pSetup->pllctrl; + SYSCON->PLL0NDEC = pSetup->pllndec; + SYSCON->PLL0NDEC = pSetup->pllndec | (1UL << SYSCON_PLL0NDEC_NREQ_SHIFT); /* latch */ + SYSCON->PLL0PDEC = pSetup->pllpdec; + SYSCON->PLL0PDEC = pSetup->pllpdec | (1UL << SYSCON_PLL0PDEC_PREQ_SHIFT); /* latch */ + SYSCON->PLL0SSCG0 = pSetup->pllsscg[0]; + SYSCON->PLL0SSCG1 = pSetup->pllsscg[1]; + SYSCON->PLL0SSCG1 = + pSetup->pllsscg[1] | (1UL << SYSCON_PLL0SSCG1_MD_REQ_SHIFT) | (1UL << SYSCON_PLL0SSCG1_MREQ_SHIFT); /* latch */ + + POWER_DisablePD(kPDRUNCFG_PD_PLL0); + POWER_DisablePD(kPDRUNCFG_PD_PLL0_SSCG); + + if ((pSetup->flags & PLL_SETUPFLAG_WAITLOCK) != 0UL) + { + if ((SYSCON->PLL0SSCG1 & SYSCON_PLL0SSCG1_MDIV_EXT_MASK) != 0UL) /* normal mode */ + { + inRate = CLOCK_GetPLL0InClockRate(); + prediv = findPll0PreDiv(); + /* Adjust input clock */ + clkRate = inRate / prediv; + /* The lock signal is only reliable between fref[2] :100 kHz to 20 MHz. */ + if ((clkRate >= 100000UL) && (clkRate <= 20000000UL)) + { + while (CLOCK_IsPLL0Locked() == false) + { + } + } + else + { + SDK_DelayAtLeastUs(6000U, + SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); /* software should use a 6 ms time interval + to insure the PLL will be stable */ + } + } + else /* spread spectrum mode */ + { + SDK_DelayAtLeastUs(6000U, + SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); /* software should use a 6 ms time interval to + insure the PLL will be stable */ + } + } + + /* Update current programmed PLL rate var */ + s_Pll0_Freq = pSetup->pllRate; + + return kStatus_PLL_Success; +} + +/* Setup PLL1 Frequency from pre-calculated value */ +/** + * brief Set PLL1 output from PLL setup structure (precise frequency) + * param pSetup : Pointer to populated PLL setup structure + * return kStatus_PLL_Success on success, or PLL setup error code + * note This function will power off the PLL, setup the PLL with the + * new setup data, and then optionally powerup the PLL, wait for PLL lock, + * and adjust system voltages to the new PLL rate. The function will not + * alter any source clocks (ie, main systen clock) that may use the PLL, + * so these should be setup prior to and after exiting the function. + */ +pll_error_t CLOCK_SetPLL1Freq(const pll_setup_t *pSetup) +{ + uint32_t inRate, clkRate, prediv; + /* Power off PLL during setup changes */ + POWER_EnablePD(kPDRUNCFG_PD_PLL1); + + /* Write PLL setup data */ + SYSCON->PLL1CTRL = pSetup->pllctrl; + SYSCON->PLL1NDEC = pSetup->pllndec; + SYSCON->PLL1NDEC = pSetup->pllndec | (1UL << SYSCON_PLL1NDEC_NREQ_SHIFT); /* latch */ + SYSCON->PLL1PDEC = pSetup->pllpdec; + SYSCON->PLL1PDEC = pSetup->pllpdec | (1UL << SYSCON_PLL1PDEC_PREQ_SHIFT); /* latch */ + SYSCON->PLL1MDEC = pSetup->pllmdec; + SYSCON->PLL1MDEC = pSetup->pllmdec | (1UL << SYSCON_PLL1MDEC_MREQ_SHIFT); /* latch */ + + POWER_DisablePD(kPDRUNCFG_PD_PLL1); + + if ((pSetup->flags & PLL_SETUPFLAG_WAITLOCK) != 0UL) + { + inRate = CLOCK_GetPLL1InClockRate(); + prediv = findPll1PreDiv(); + /* Adjust input clock */ + clkRate = inRate / prediv; + /* The lock signal is only reliable between fref[2] :100 kHz to 20 MHz. */ + if ((clkRate >= 100000UL) && (clkRate <= 20000000UL)) + { + while (CLOCK_IsPLL1Locked() == false) + { + } + } + else + { + SDK_DelayAtLeastUs(6000U, + SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); /* software should use a 6 ms time interval to + insure the PLL will be stable */ + } + } + + /* Update current programmed PLL rate var */ + s_Pll1_Freq = pSetup->pllRate; + + return kStatus_PLL_Success; +} + +/* Set PLL0 clock based on the input frequency and multiplier */ +/*! brief Set PLL0 output based on the multiplier and input frequency + * param multiply_by : multiplier + * param input_freq : Clock input frequency of the PLL + * return Nothing + * note Unlike the Chip_Clock_SetupSystemPLLPrec() function, this + * function does not disable or enable PLL power, wait for PLL lock, + * or adjust system voltages. These must be done in the application. + * The function will not alter any source clocks (ie, main systen clock) + * that may use the PLL, so these should be setup prior to and after + * exiting the function. + */ +void CLOCK_SetupPLL0Mult(uint32_t multiply_by, uint32_t input_freq) +{ + uint32_t cco_freq = input_freq * multiply_by; + uint32_t pdec = 1U; + uint32_t selr; + uint32_t seli; + uint32_t selp; + uint32_t mdec, ndec; + + while (cco_freq < 275000000U) + { + multiply_by <<= 1U; /* double value in each iteration */ + pdec <<= 1U; /* correspondingly double pdec to cancel effect of double msel */ + cco_freq = input_freq * multiply_by; + } + + selr = 0U; + + if (multiply_by >= 8000UL) + { + seli = 1UL; + } + else if (multiply_by >= 122UL) + { + seli = (uint32_t)(8000UL / multiply_by); /*floor(8000/M) */ + } + else + { + seli = 2UL * ((uint32_t)(multiply_by / 4UL)) + 3UL; /* 2*floor(M/4) + 3 */ + } + + if (seli >= 63U) + { + seli = 63U; + } + + { + selp = 31U; + } + + if (pdec > 1U) + { + pdec = pdec / 2U; /* Account for minus 1 encoding */ + /* Translate P value */ + } + + mdec = (uint32_t)PLL_SSCG1_MDEC_VAL_SET(multiply_by); + ndec = 0x1U; /* pre divide by 1 (hardcoded) */ + + SYSCON->PLL0CTRL = SYSCON_PLL0CTRL_CLKEN_MASK | SYSCON_PLL0CTRL_BYPASSPOSTDIV(0) | + SYSCON_PLL0CTRL_BYPASSPOSTDIV2(0) | (selr << SYSCON_PLL0CTRL_SELR_SHIFT) | + (seli << SYSCON_PLL0CTRL_SELI_SHIFT) | (selp << SYSCON_PLL0CTRL_SELP_SHIFT); + SYSCON->PLL0PDEC = pdec | (1UL << SYSCON_PLL0PDEC_PREQ_SHIFT); /* set Pdec value and assert preq */ + SYSCON->PLL0NDEC = ndec | (1UL << SYSCON_PLL0NDEC_NREQ_SHIFT); /* set Pdec value and assert preq */ + SYSCON->PLL0SSCG1 = + mdec | (1UL << SYSCON_PLL0SSCG1_MREQ_SHIFT); /* select non sscg MDEC value, assert mreq and select mdec value */ +} + +/* Enable USB DEVICE FULL SPEED clock */ +/*! brief Enable USB Device FS clock. + * param src : clock source + * param freq: clock frequency + * Enable USB Device Full Speed clock. + */ +bool CLOCK_EnableUsbfs0DeviceClock(clock_usbfs_src_t src, uint32_t freq) +{ + bool ret = true; + + CLOCK_DisableClock(kCLOCK_Usbd0); + + if (kCLOCK_UsbfsSrcFro == src) + { + switch (freq) + { + case 96000000U: + CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 2, false); /*!< Div by 2 to get 48MHz, no divider reset */ + break; + + default: + ret = false; + break; + } + /* Turn ON FRO HF */ + POWER_DisablePD(kPDRUNCFG_PD_FRO192M); + /* Enable FRO 96MHz output */ + ANACTRL->FRO192M_CTRL = + ANACTRL->FRO192M_CTRL | ANACTRL_FRO192M_CTRL_ENA_96MHZCLK_MASK | ANACTRL_FRO192M_CTRL_USBCLKADJ_MASK; + /* Select FRO 96 or 48 MHz */ + CLOCK_AttachClk(kFRO_HF_to_USB0_CLK); + } + else + { + /*!< Configure XTAL32M */ + POWER_DisablePD(kPDRUNCFG_PD_XTAL32M); /* Ensure XTAL32M is powered */ + POWER_DisablePD(kPDRUNCFG_PD_LDOXO32M); /* Ensure XTAL32M is powered */ + (void)CLOCK_SetupExtClocking(16000000U); /* Enable clk_in clock */ + SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK; /* Enable clk_in from XTAL32M clock */ + ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_SYSTEM_CLK_OUT_MASK; /* Enable clk_in to system */ + + /*!< Set up PLL1 */ + POWER_DisablePD(kPDRUNCFG_PD_PLL1); + CLOCK_AttachClk(kEXT_CLK_to_PLL1); /*!< Switch PLL1CLKSEL to EXT_CLK */ + const pll_setup_t pll1Setup = { + .pllctrl = SYSCON_PLL1CTRL_CLKEN_MASK | SYSCON_PLL1CTRL_SELI(19U) | SYSCON_PLL1CTRL_SELP(9U), + .pllndec = SYSCON_PLL1NDEC_NDIV(1U), + .pllpdec = SYSCON_PLL1PDEC_PDIV(5U), + .pllmdec = SYSCON_PLL1MDEC_MDIV(30U), + .pllRate = 48000000U, + .flags = PLL_SETUPFLAG_WAITLOCK}; + (void)CLOCK_SetPLL1Freq(&pll1Setup); + + CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 1U, false); + CLOCK_AttachClk(kPLL1_to_USB0_CLK); + SDK_DelayAtLeastUs(50U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); + } + CLOCK_EnableClock(kCLOCK_Usbd0); + CLOCK_EnableClock(kCLOCK_UsbRam1); + + return ret; +} + +/* Enable USB HOST FULL SPEED clock */ +/*! brief Enable USB HOST FS clock. + * param src : clock source + * param freq: clock frequency + * Enable USB HOST Full Speed clock. + */ +bool CLOCK_EnableUsbfs0HostClock(clock_usbfs_src_t src, uint32_t freq) +{ + bool ret = true; + + CLOCK_DisableClock(kCLOCK_Usbhmr0); + CLOCK_DisableClock(kCLOCK_Usbhsl0); + + if (kCLOCK_UsbfsSrcFro == src) + { + switch (freq) + { + case 96000000U: + CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 2, false); /*!< Div by 2 to get 48MHz, no divider reset */ + break; + + default: + ret = false; + break; + } + /* Turn ON FRO HF */ + POWER_DisablePD(kPDRUNCFG_PD_FRO192M); + /* Enable FRO 96MHz output */ + ANACTRL->FRO192M_CTRL = ANACTRL->FRO192M_CTRL | ANACTRL_FRO192M_CTRL_ENA_96MHZCLK_MASK; + /* Select FRO 96 MHz */ + CLOCK_AttachClk(kFRO_HF_to_USB0_CLK); + } + else + { + /*!< Configure XTAL32M */ + POWER_DisablePD(kPDRUNCFG_PD_XTAL32M); /* Ensure XTAL32M is powered */ + POWER_DisablePD(kPDRUNCFG_PD_LDOXO32M); /* Ensure XTAL32M is powered */ + (void)CLOCK_SetupExtClocking(16000000U); /* Enable clk_in clock */ + SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK; /* Enable clk_in from XTAL32M clock */ + ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_SYSTEM_CLK_OUT_MASK; /* Enable clk_in to system */ + + /*!< Set up PLL1 */ + POWER_DisablePD(kPDRUNCFG_PD_PLL1); + CLOCK_AttachClk(kEXT_CLK_to_PLL1); /*!< Switch PLL1CLKSEL to EXT_CLK */ + const pll_setup_t pll1Setup = { + .pllctrl = SYSCON_PLL1CTRL_CLKEN_MASK | SYSCON_PLL1CTRL_SELI(19U) | SYSCON_PLL1CTRL_SELP(9U), + .pllndec = SYSCON_PLL1NDEC_NDIV(1U), + .pllpdec = SYSCON_PLL1PDEC_PDIV(5U), + .pllmdec = SYSCON_PLL1MDEC_MDIV(30U), + .pllRate = 48000000U, + .flags = PLL_SETUPFLAG_WAITLOCK}; + (void)CLOCK_SetPLL1Freq(&pll1Setup); + + CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 1U, false); + CLOCK_AttachClk(kPLL1_to_USB0_CLK); + SDK_DelayAtLeastUs(50U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); + } + CLOCK_EnableClock(kCLOCK_Usbhmr0); + CLOCK_EnableClock(kCLOCK_Usbhsl0); + CLOCK_EnableClock(kCLOCK_UsbRam1); + + return ret; +} + +/* Enable USB PHY clock */ +bool CLOCK_EnableUsbhs0PhyPllClock(clock_usb_phy_src_t src, uint32_t freq) +{ + volatile uint32_t i; + uint32_t phyPllDiv = 0U; + uint16_t multiplier = 0U; + + POWER_DisablePD(kPDRUNCFG_PD_XTAL32M); + POWER_DisablePD(kPDRUNCFG_PD_LDOXO32M); + POWER_DisablePD(kPDRUNCFG_PD_FRO32K); /*!< Ensure FRO32k is on */ + POWER_DisablePD(kPDRUNCFG_PD_XTAL32K); /*!< Ensure xtal32k is on */ + POWER_DisablePD(kPDRUNCFG_PD_USB1_PHY); /*!< Ensure xtal32k is on */ + POWER_DisablePD(kPDRUNCFG_PD_LDOUSBHS); /*!< Ensure xtal32k is on */ + + /* wait to make sure PHY power is fully up */ + i = 100000U; + while ((i--) != 0U) + { + __ASM("nop"); + } + + SYSCON->AHBCLKCTRLSET[2] = SYSCON_AHBCLKCTRL2_ANALOG_CTRL(1); + SYSCON->AHBCLKCTRLSET[2] = SYSCON_AHBCLKCTRL2_USB1_PHY(1); + + USBPHY->CTRL_CLR = USBPHY_CTRL_SFTRST_MASK; + + multiplier = 480000000 / freq; + + switch (multiplier) + { + case 15: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(0U); + break; + } + case 16: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(1U); + break; + } + case 20: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(2U); + break; + } + case 24: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(4U); + break; + } + case 25: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(5U); + break; + } + case 30: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(6U); + break; + } + default: + { + return false; + } + } + USBPHY->PLL_SIC = (USBPHY->PLL_SIC & ~USBPHY_PLL_SIC_PLL_DIV_SEL(0x7)) | phyPllDiv; + USBPHY->PLL_SIC_SET = USBPHY_PLL_SIC_SET_PLL_REG_ENABLE_MASK; + USBPHY->PLL_SIC_CLR = (1UL << 16U); // Reserved. User must set this bit to 0x0 + USBPHY->PLL_SIC_SET = USBPHY_PLL_SIC_SET_PLL_POWER_MASK; + USBPHY->PLL_SIC_SET = USBPHY_PLL_SIC_SET_PLL_EN_USB_CLKS_MASK; + + USBPHY->CTRL_CLR = USBPHY_CTRL_CLR_CLKGATE_MASK; + USBPHY->PWD_SET = 0x0; + + return true; +} + +/* Enable USB DEVICE HIGH SPEED clock */ +bool CLOCK_EnableUsbhs0DeviceClock(clock_usbhs_src_t src, uint32_t freq) +{ + SYSCON->AHBCLKCTRLSET[2] = SYSCON_AHBCLKCTRL2_USB1_RAM(1); + SYSCON->AHBCLKCTRLSET[2] = SYSCON_AHBCLKCTRL2_USB1_DEV(1); + + /* 16 MHz will be driven by the tb on the xtal1 pin of XTAL32M */ + SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK; /* Enable clock_in clock for clock module. */ + ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_PLL_USB_OUT(1); + return true; +} + +/* Enable USB HOST HIGH SPEED clock */ +bool CLOCK_EnableUsbhs0HostClock(clock_usbhs_src_t src, uint32_t freq) +{ + SYSCON->AHBCLKCTRLSET[2] = SYSCON_AHBCLKCTRL2_USB1_RAM(1); + SYSCON->AHBCLKCTRLSET[2] = SYSCON_AHBCLKCTRL2_USB1_HOST(1); + + /* 16 MHz will be driven by the tb on the xtal1 pin of XTAL32M */ + SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK; /* Enable clock_in clock for clock module. */ + ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_PLL_USB_OUT(1); + + return true; +} + +/*! @brief Enable the OSTIMER 32k clock. + * @return Nothing + */ +void CLOCK_EnableOstimer32kClock(void) +{ + PMC->OSTIMERr |= PMC_OSTIMER_CLOCKENABLE_MASK; +} diff --git a/drivers/fsl_clock.h b/drivers/fsl_clock.h new file mode 100644 index 0000000..f35ff1e --- /dev/null +++ b/drivers/fsl_clock.h @@ -0,0 +1,1525 @@ +/* + * Copyright 2017 - 2021 , NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _FSL_CLOCK_H_ +#define _FSL_CLOCK_H_ + +#include "fsl_common.h" + +/*! @addtogroup clock */ +/*! @{ */ + +/*! @file */ + +/******************************************************************************* + * Definitions + *****************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +/*! @brief CLOCK driver version 2.3.7. */ +#define FSL_CLOCK_DRIVER_VERSION (MAKE_VERSION(2, 3, 7)) +/*@}*/ + +/*! @brief Configure whether driver controls clock + * + * When set to 0, peripheral drivers will enable clock in initialize function + * and disable clock in de-initialize function. When set to 1, peripheral + * driver will not control the clock, application could control the clock out of + * the driver. + * + * @note All drivers share this feature switcher. If it is set to 1, application + * should handle clock enable and disable for all drivers. + */ +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)) +#define FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL 0 +#endif + +/*! + * @brief User-defined the size of cache for CLOCK_PllGetConfig() function. + * + * Once define this MACRO to be non-zero value, CLOCK_PllGetConfig() function + * would cache the recent calulation and accelerate the execution to get the + * right settings. + */ +#ifndef CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT +#define CLOCK_USR_CFG_PLL_CONFIG_CACHE_COUNT 2U +#endif + +/* Definition for delay API in clock driver, users can redefine it to the real application. */ +#ifndef SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY +#define SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY (100000000UL) +#endif + +/*! @brief Clock ip name array for ROM. */ +#define ROM_CLOCKS \ + { \ + kCLOCK_Rom \ + } +/*! @brief Clock ip name array for SRAM. */ +#define SRAM_CLOCKS \ + { \ + kCLOCK_Sram1, kCLOCK_Sram2 \ + } +/*! @brief Clock ip name array for FLASH. */ +#define FLASH_CLOCKS \ + { \ + kCLOCK_Flash \ + } +/*! @brief Clock ip name array for FMC. */ +#define FMC_CLOCKS \ + { \ + kCLOCK_Fmc \ + } +/*! @brief Clock ip name array for INPUTMUX. */ +#define INPUTMUX_CLOCKS \ + { \ + kCLOCK_InputMux0 \ + } +/*! @brief Clock ip name array for IOCON. */ +#define IOCON_CLOCKS \ + { \ + kCLOCK_Iocon \ + } +/*! @brief Clock ip name array for GPIO. */ +#define GPIO_CLOCKS \ + { \ + kCLOCK_Gpio0, kCLOCK_Gpio1 \ + } +/*! @brief Clock ip name array for PINT. */ +#define PINT_CLOCKS \ + { \ + kCLOCK_Pint \ + } +/*! @brief Clock ip name array for GINT. */ +#define GINT_CLOCKS \ + { \ + kCLOCK_Gint, kCLOCK_Gint \ + } +/*! @brief Clock ip name array for DMA. */ +#define DMA_CLOCKS \ + { \ + kCLOCK_Dma0, kCLOCK_Dma1 \ + } +/*! @brief Clock ip name array for CRC. */ +#define CRC_CLOCKS \ + { \ + kCLOCK_Crc \ + } +/*! @brief Clock ip name array for WWDT. */ +#define WWDT_CLOCKS \ + { \ + kCLOCK_Wwdt \ + } +/*! @brief Clock ip name array for RTC. */ +#define RTC_CLOCKS \ + { \ + kCLOCK_Rtc \ + } +/*! @brief Clock ip name array for Mailbox. */ +#define MAILBOX_CLOCKS \ + { \ + kCLOCK_Mailbox \ + } +/*! @brief Clock ip name array for LPADC. */ +#define LPADC_CLOCKS \ + { \ + kCLOCK_Adc0 \ + } +/*! @brief Clock ip name array for MRT. */ +#define MRT_CLOCKS \ + { \ + kCLOCK_Mrt \ + } +/*! @brief Clock ip name array for OSTIMER. */ +#define OSTIMER_CLOCKS \ + { \ + kCLOCK_OsTimer0 \ + } +/*! @brief Clock ip name array for SCT0. */ +#define SCT_CLOCKS \ + { \ + kCLOCK_Sct0 \ + } +/*! @brief Clock ip name array for MCAN. */ +#define MCAN_CLOCKS \ + { \ + kCLOCK_Mcan \ + } +/*! @brief Clock ip name array for UTICK. */ +#define UTICK_CLOCKS \ + { \ + kCLOCK_Utick0 \ + } +/*! @brief Clock ip name array for FLEXCOMM. */ +#define FLEXCOMM_CLOCKS \ + { \ + kCLOCK_FlexComm0, kCLOCK_FlexComm1, kCLOCK_FlexComm2, kCLOCK_FlexComm3, kCLOCK_FlexComm4, kCLOCK_FlexComm5, \ + kCLOCK_FlexComm6, kCLOCK_FlexComm7, kCLOCK_Hs_Lspi \ + } +/*! @brief Clock ip name array for LPUART. */ +#define LPUART_CLOCKS \ + { \ + kCLOCK_MinUart0, kCLOCK_MinUart1, kCLOCK_MinUart2, kCLOCK_MinUart3, kCLOCK_MinUart4, kCLOCK_MinUart5, \ + kCLOCK_MinUart6, kCLOCK_MinUart7 \ + } + +/*! @brief Clock ip name array for BI2C. */ +#define BI2C_CLOCKS \ + { \ + kCLOCK_BI2c0, kCLOCK_BI2c1, kCLOCK_BI2c2, kCLOCK_BI2c3, kCLOCK_BI2c4, kCLOCK_BI2c5, kCLOCK_BI2c6, kCLOCK_BI2c7 \ + } +/*! @brief Clock ip name array for LSPI. */ +#define LPSPI_CLOCKS \ + { \ + kCLOCK_LSpi0, kCLOCK_LSpi1, kCLOCK_LSpi2, kCLOCK_LSpi3, kCLOCK_LSpi4, kCLOCK_LSpi5, kCLOCK_LSpi6, kCLOCK_LSpi7 \ + } +/*! @brief Clock ip name array for FLEXI2S. */ +#define FLEXI2S_CLOCKS \ + { \ + kCLOCK_FlexI2s0, kCLOCK_FlexI2s1, kCLOCK_FlexI2s2, kCLOCK_FlexI2s3, kCLOCK_FlexI2s4, kCLOCK_FlexI2s5, \ + kCLOCK_FlexI2s6, kCLOCK_FlexI2s7 \ + } +/*! @brief Clock ip name array for CTIMER. */ +#define CTIMER_CLOCKS \ + { \ + kCLOCK_Timer0, kCLOCK_Timer1, kCLOCK_Timer2, kCLOCK_Timer3, kCLOCK_Timer4 \ + } +/*! @brief Clock ip name array for EZHA */ +#define EZHA_CLOCKS \ + { \ + kCLOCK_Ezha \ + } +/*! @brief Clock ip name array for EZHB */ +#define EZHB_CLOCKS \ + { \ + kCLOCK_Ezhb \ + } +/*! @brief Clock ip name array for COMP */ +#define COMP_CLOCKS \ + { \ + kCLOCK_Comp \ + } +/*! @brief Clock ip name array for USB1CLK. */ +#define USB1CLK_CLOCKS \ + { \ + kCLOCK_Usb1Clk \ + } +/*! @brief Clock ip name array for FREQME. */ +#define FREQME_CLOCKS \ + { \ + kCLOCK_Freqme \ + } +/*! @brief Clock ip name array for USBRAM. */ +#define USBRAM_CLOCKS \ + { \ + kCLOCK_UsbRam1 \ + } +/*! @brief Clock ip name array for CDOG. */ +#define CDOG_CLOCKS \ + { \ + kCLOCK_Cdog \ + } +/*! @brief Clock ip name array for RNG. */ +#define RNG_CLOCKS \ + { \ + kCLOCK_Rng \ + } +/*! @brief Clock ip name array for USBHMR0. */ +#define USBHMR0_CLOCKS \ + { \ + kCLOCK_Usbhmr0 \ + } +/*! @brief Clock ip name array for USBHSL0. */ +#define USBHSL0_CLOCKS \ + { \ + kCLOCK_Usbhsl0 \ + } +/*! @brief Clock ip name array for HashCrypt. */ +#define HASHCRYPT_CLOCKS \ + { \ + kCLOCK_HashCrypt \ + } +/*! @brief Clock ip name array for PLULUT. */ +#define PLULUT_CLOCKS \ + { \ + kCLOCK_PluLut \ + } +/*! @brief Clock ip name array for PUF. */ +#define PUF_CLOCKS \ + { \ + kCLOCK_Puf \ + } +/*! @brief Clock ip name array for CASPER. */ +#define CASPER_CLOCKS \ + { \ + kCLOCK_Casper \ + } +/*! @brief Clock ip name array for ANALOGCTRL. */ +#define ANALOGCTRL_CLOCKS \ + { \ + kCLOCK_AnalogCtrl \ + } +/*! @brief Clock ip name array for HS_LSPI. */ +#define HS_LSPI_CLOCKS \ + { \ + kCLOCK_Hs_Lspi \ + } +/*! @brief Clock ip name array for GPIO_SEC. */ +#define GPIO_SEC_CLOCKS \ + { \ + kCLOCK_Gpio_Sec \ + } +/*! @brief Clock ip name array for GPIO_SEC_INT. */ +#define GPIO_SEC_INT_CLOCKS \ + { \ + kCLOCK_Gpio_Sec_Int \ + } +/*! @brief Clock ip name array for USBD. */ +#define USBD_CLOCKS \ + { \ + kCLOCK_Usbd0, kCLOCK_Usbh1, kCLOCK_Usbd1 \ + } +/*! @brief Clock ip name array for USBH. */ +#define USBH_CLOCKS \ + { \ + kCLOCK_Usbh1 \ + } +#define PLU_CLOCKS \ + { \ + kCLOCK_PluLut \ + } +#define SYSCTL_CLOCKS \ + { \ + kCLOCK_Sysctl \ + } +/*! @brief Clock gate name used for CLOCK_EnableClock/CLOCK_DisableClock. */ +/*------------------------------------------------------------------------------ + clock_ip_name_t definition: +------------------------------------------------------------------------------*/ + +#define CLK_GATE_REG_OFFSET_SHIFT 8U +#define CLK_GATE_REG_OFFSET_MASK 0xFFFFFF00U +#define CLK_GATE_BIT_SHIFT_SHIFT 0U +#define CLK_GATE_BIT_SHIFT_MASK 0x000000FFU + +#define CLK_GATE_DEFINE(reg_offset, bit_shift) \ + ((((reg_offset) << CLK_GATE_REG_OFFSET_SHIFT) & CLK_GATE_REG_OFFSET_MASK) | \ + (((bit_shift) << CLK_GATE_BIT_SHIFT_SHIFT) & CLK_GATE_BIT_SHIFT_MASK)) + +#define CLK_GATE_ABSTRACT_REG_OFFSET(x) (((uint32_t)(x)&CLK_GATE_REG_OFFSET_MASK) >> CLK_GATE_REG_OFFSET_SHIFT) +#define CLK_GATE_ABSTRACT_BITS_SHIFT(x) (((uint32_t)(x)&CLK_GATE_BIT_SHIFT_MASK) >> CLK_GATE_BIT_SHIFT_SHIFT) + +#define AHB_CLK_CTRL0 0 +#define AHB_CLK_CTRL1 1 +#define AHB_CLK_CTRL2 2 + +/*! @brief Clock gate name used for CLOCK_EnableClock/CLOCK_DisableClock. */ +typedef enum _clock_ip_name +{ + kCLOCK_IpInvalid = 0U, /*!< Invalid Ip Name. */ + kCLOCK_Rom = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 1), /*!< Clock gate name: Rom. */ + + kCLOCK_Sram1 = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 3), /*!< Clock gate name: Sram1. */ + + kCLOCK_Sram2 = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 4), /*!< Clock gate name: Sram2. */ + + kCLOCK_Flash = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 7), /*!< Clock gate name: Flash. */ + + kCLOCK_Fmc = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 8), /*!< Clock gate name: Fmc. */ + + kCLOCK_InputMux = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 11), /*!< Clock gate name: InputMux. */ + + kCLOCK_Iocon = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 13), /*!< Clock gate name: Iocon. */ + + kCLOCK_Gpio0 = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 14), /*!< Clock gate name: Gpio0. */ + + kCLOCK_Gpio1 = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 15), /*!< Clock gate name: Gpio1. */ + + kCLOCK_Pint = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 18), /*!< Clock gate name: Pint. */ + + kCLOCK_Gint = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 19), /*!< Clock gate name: Gint. */ + + kCLOCK_Dma0 = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 20), /*!< Clock gate name: Dma0. */ + + kCLOCK_Crc = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 21), /*!< Clock gate name: Crc. */ + + kCLOCK_Wwdt = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 22), /*!< Clock gate name: Wwdt. */ + + kCLOCK_Rtc = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 23), /*!< Clock gate name: Rtc. */ + + kCLOCK_Mailbox = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 26), /*!< Clock gate name: Mailbox. */ + + kCLOCK_Adc0 = CLK_GATE_DEFINE(AHB_CLK_CTRL0, 27), /*!< Clock gate name: Adc0. */ + + kCLOCK_Mrt = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 0), /*!< Clock gate name: Mrt. */ + + kCLOCK_OsTimer0 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 1), /*!< Clock gate name: OsTimer0. */ + + kCLOCK_Sct0 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 2), /*!< Clock gate name: Sct0. */ + + kCLOCK_Mcan = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 7), /*!< Clock gate name: Mcan. */ + + kCLOCK_Utick0 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 10), /*!< Clock gate name: Utick0. */ + + kCLOCK_FlexComm0 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 11), /*!< Clock gate name: FlexComm0. */ + + kCLOCK_FlexComm1 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 12), /*!< Clock gate name: FlexComm1. */ + + kCLOCK_FlexComm2 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 13), /*!< Clock gate name: FlexComm2. */ + + kCLOCK_FlexComm3 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 14), /*!< Clock gate name: FlexComm3. */ + + kCLOCK_FlexComm4 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 15), /*!< Clock gate name: FlexComm4. */ + + kCLOCK_FlexComm5 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 16), /*!< Clock gate name: FlexComm5. */ + + kCLOCK_FlexComm6 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 17), /*!< Clock gate name: FlexComm6. */ + + kCLOCK_FlexComm7 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 18), /*!< Clock gate name: FlexComm7. */ + + kCLOCK_MinUart0 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 11), /*!< Clock gate name: MinUart0. */ + + kCLOCK_MinUart1 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 12), /*!< Clock gate name: MinUart1. */ + + kCLOCK_MinUart2 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 13), /*!< Clock gate name: MinUart2. */ + + kCLOCK_MinUart3 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 14), /*!< Clock gate name: MinUart3. */ + + kCLOCK_MinUart4 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 15), /*!< Clock gate name: MinUart4. */ + + kCLOCK_MinUart5 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 16), /*!< Clock gate name: MinUart5. */ + + kCLOCK_MinUart6 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 17), /*!< Clock gate name: MinUart6. */ + + kCLOCK_MinUart7 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 18), /*!< Clock gate name: MinUart7. */ + + kCLOCK_LSpi0 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 11), /*!< Clock gate name: LSpi0. */ + + kCLOCK_LSpi1 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 12), /*!< Clock gate name: LSpi1. */ + + kCLOCK_LSpi2 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 13), /*!< Clock gate name: LSpi2. */ + + kCLOCK_LSpi3 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 14), /*!< Clock gate name: LSpi3. */ + + kCLOCK_LSpi4 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 15), /*!< Clock gate name: LSpi4. */ + + kCLOCK_LSpi5 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 16), /*!< Clock gate name: LSpi5. */ + + kCLOCK_LSpi6 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 17), /*!< Clock gate name: LSpi6. */ + + kCLOCK_LSpi7 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 18), /*!< Clock gate name: LSpi7. */ + + kCLOCK_BI2c0 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 11), /*!< Clock gate name: BI2c0. */ + + kCLOCK_BI2c1 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 12), /*!< Clock gate name: BI2c1. */ + + kCLOCK_BI2c2 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 13), /*!< Clock gate name: BI2c2. */ + + kCLOCK_BI2c3 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 14), /*!< Clock gate name: BI2c3. */ + + kCLOCK_BI2c4 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 15), /*!< Clock gate name: BI2c4. */ + + kCLOCK_BI2c5 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 16), /*!< Clock gate name: BI2c5. */ + + kCLOCK_BI2c6 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 17), /*!< Clock gate name: BI2c6. */ + + kCLOCK_BI2c7 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 18), /*!< Clock gate name: BI2c7. */ + + kCLOCK_FlexI2s0 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 11), /*!< Clock gate name: FlexI2s0. */ + + kCLOCK_FlexI2s1 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 12), /*!< Clock gate name: FlexI2s1. */ + + kCLOCK_FlexI2s2 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 13), /*!< Clock gate name: FlexI2s2. */ + + kCLOCK_FlexI2s3 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 14), /*!< Clock gate name: FlexI2s3. */ + + kCLOCK_FlexI2s4 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 15), /*!< Clock gate name: FlexI2s4. */ + + kCLOCK_FlexI2s5 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 16), /*!< Clock gate name: FlexI2s5. */ + + kCLOCK_FlexI2s6 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 17), /*!< Clock gate name: FlexI2s6. */ + + kCLOCK_FlexI2s7 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 18), /*!< Clock gate name: FlexI2s7. */ + + kCLOCK_Timer2 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 22), /*!< Clock gate name: Timer2. */ + + kCLOCK_Usbd0 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 25), /*!< Clock gate name: Usbd0. */ + + kCLOCK_Timer0 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 26), /*!< Clock gate name: Timer0. */ + + kCLOCK_Timer1 = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 27), /*!< Clock gate name: Timer1. */ + + kCLOCK_Ezha = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 30), /*!< Clock gate name: Ezha. */ + + kCLOCK_Ezhb = CLK_GATE_DEFINE(AHB_CLK_CTRL1, 31), /*!< Clock gate name: Ezhb. */ + + kCLOCK_Dma1 = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 1), /*!< Clock gate name: Dma1. */ + + kCLOCK_Comp = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 2), /*!< Clock gate name: Comp. */ + + kCLOCK_Usbh1 = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 4), /*!< Clock gate name: Usbh1. */ + + kCLOCK_Usbd1 = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 5), /*!< Clock gate name: Usbd1. */ + + kCLOCK_UsbRam1 = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 6), /*!< Clock gate name: UsbRam1. */ + + kCLOCK_Usb1Clk = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 7), /*!< Clock gate name: Usb1Clk. */ + + kCLOCK_Freqme = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 8), /*!< Clock gate name: Freqme. */ + + kCLOCK_Cdog = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 11), /*!< Clock gate name: Cdog. */ + + kCLOCK_Rng = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 13), /*!< Clock gate name: Rng. */ + + kCLOCK_Sysctl = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 15), /*!< Clock gate name: Sysctl. */ + + kCLOCK_Usbhmr0 = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 16), /*!< Clock gate name: Usbhmr0. */ + + kCLOCK_Usbhsl0 = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 17), /*!< Clock gate name: Usbhsl0. */ + + kCLOCK_HashCrypt = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 18), /*!< Clock gate name: HashCrypt. */ + + kCLOCK_PluLut = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 20), /*!< Clock gate name: PluLut. */ + + kCLOCK_Timer3 = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 21), /*!< Clock gate name: Timer3. */ + + kCLOCK_Timer4 = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 22), /*!< Clock gate name: Timer4. */ + + kCLOCK_Puf = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 23), /*!< Clock gate name: Puf. */ + + kCLOCK_Casper = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 24), /*!< Clock gate name: Casper. */ + + kCLOCK_AnalogCtrl = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 27), /*!< Clock gate name: AnalogCtrl. */ + + kCLOCK_Hs_Lspi = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 28), /*!< Clock gate name: Lspi. */ + + kCLOCK_Gpio_Sec = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 29), /*!< Clock gate name: GPIO Sec. */ + + kCLOCK_Gpio_Sec_Int = CLK_GATE_DEFINE(AHB_CLK_CTRL2, 30) /*!< Clock gate name: GPIO SEC Int. */ +} clock_ip_name_t; + +/*! @brief Peripherals clock source definition. */ +#define BUS_CLK kCLOCK_BusClk + +#define I2C0_CLK_SRC BUS_CLK + +/*! @brief Clock name used to get clock frequency. */ +typedef enum _clock_name +{ + kCLOCK_CoreSysClk, /*!< Core/system clock (aka MAIN_CLK) */ + kCLOCK_BusClk, /*!< Bus clock (AHB clock) */ + kCLOCK_ClockOut, /*!< CLOCKOUT */ + kCLOCK_FroHf, /*!< FRO48/96 */ + kCLOCK_Pll1Out, /*!< PLL1 Output */ + kCLOCK_Mclk, /*!< MCLK */ + kCLOCK_Fro12M, /*!< FRO12M */ + kCLOCK_Fro1M, /*!< FRO1M */ + kCLOCK_ExtClk, /*!< External Clock */ + kCLOCK_Pll0Out, /*!< PLL0 Output */ + kCLOCK_FlexI2S, /*!< FlexI2S clock */ + +} clock_name_t; + +/*! @brief Clock Mux Switches + * The encoding is as follows each connection identified is 32bits wide while 24bits are valuable + * starting from LSB upwards + * + * [4 bits for choice, 0 means invalid choice] [8 bits mux ID]* + * + */ + +#define CLK_ATTACH_ID(mux, sel, pos) \ + ((((uint32_t)(mux) << 0U) | (((uint32_t)(sel) + 1U) & 0xFU) << 8U) << ((uint32_t)(pos)*12U)) +#define MUX_A(mux, sel) CLK_ATTACH_ID((mux), (sel), 0U) +#define MUX_B(mux, sel, selector) (CLK_ATTACH_ID((mux), (sel), 1U) | ((selector) << 24U)) + +#define GET_ID_ITEM(connection) ((connection)&0xFFFU) +#define GET_ID_NEXT_ITEM(connection) ((connection) >> 12U) +#define GET_ID_ITEM_MUX(connection) (((uint8_t)connection) & 0xFFU) +#define GET_ID_ITEM_SEL(connection) ((uint8_t)((((uint32_t)(connection)&0xF00U) >> 8U) - 1U)) +#define GET_ID_SELECTOR(connection) ((connection)&0xF000000U) + +#define CM_SYSTICKCLKSEL0 0 +#define CM_TRACECLKSEL 2 +#define CM_CTIMERCLKSEL0 3 +#define CM_CTIMERCLKSEL1 4 +#define CM_CTIMERCLKSEL2 5 +#define CM_CTIMERCLKSEL3 6 +#define CM_CTIMERCLKSEL4 7 +#define CM_MAINCLKSELA 8 +#define CM_MAINCLKSELB 9 +#define CM_CLKOUTCLKSEL 10 +#define CM_PLL0CLKSEL 12 +#define CM_PLL1CLKSEL 13 +#define CM_MCANCLKSEL 16 +#define CM_ADCASYNCCLKSEL 17 +#define CM_USB0CLKSEL 18 +#define CM_CLK32KCLKSEL 19 +#define CM_FXCOMCLKSEL0 20 +#define CM_FXCOMCLKSEL1 21 +#define CM_FXCOMCLKSEL2 22 +#define CM_FXCOMCLKSEL3 23 +#define CM_FXCOMCLKSEL4 24 +#define CM_FXCOMCLKSEL5 25 +#define CM_FXCOMCLKSEL6 26 +#define CM_FXCOMCLKSEL7 27 +#define CM_HSLSPICLKSEL 28 +#define CM_MCLKCLKSEL 32 +#define CM_SCTCLKSEL 36 + +#define CM_OSTIMERCLKSEL (62U) +#define CM_RTCOSC32KCLKSEL 63U + +/*! + * @brief The enumerator of clock attach Id. + */ +typedef enum _clock_attach_id +{ + + kFRO12M_to_MAIN_CLK = MUX_A(CM_MAINCLKSELA, 0) | MUX_B(CM_MAINCLKSELB, 0, 0), /*!< Attach FRO12M to MAIN_CLK. */ + + kEXT_CLK_to_MAIN_CLK = MUX_A(CM_MAINCLKSELA, 1) | MUX_B(CM_MAINCLKSELB, 0, 0), /*!< Attach EXT_CLK to MAIN_CLK. */ + + kFRO1M_to_MAIN_CLK = MUX_A(CM_MAINCLKSELA, 2) | MUX_B(CM_MAINCLKSELB, 0, 0), /*!< Attach FRO1M to MAIN_CLK. */ + + kFRO_HF_to_MAIN_CLK = MUX_A(CM_MAINCLKSELA, 3) | MUX_B(CM_MAINCLKSELB, 0, 0), /*!< Attach FRO_HF to MAIN_CLK. */ + + kPLL0_to_MAIN_CLK = MUX_A(CM_MAINCLKSELA, 0) | MUX_B(CM_MAINCLKSELB, 1, 0), /*!< Attach PLL0 to MAIN_CLK. */ + + kPLL1_to_MAIN_CLK = MUX_A(CM_MAINCLKSELA, 0) | MUX_B(CM_MAINCLKSELB, 2, 0), /*!< Attach PLL1 to MAIN_CLK. */ + + kOSC32K_to_MAIN_CLK = MUX_A(CM_MAINCLKSELA, 0) | MUX_B(CM_MAINCLKSELB, 3, 0), /*!< Attach OSC32K to MAIN_CLK. */ + + kMAIN_CLK_to_CLKOUT = MUX_A(CM_CLKOUTCLKSEL, 0), /*!< Attach MAIN_CLK to CLKOUT. */ + + kPLL0_to_CLKOUT = MUX_A(CM_CLKOUTCLKSEL, 1), /*!< Attach PLL0 to CLKOUT. */ + + kEXT_CLK_to_CLKOUT = MUX_A(CM_CLKOUTCLKSEL, 2), /*!< Attach EXT_CLK to CLKOUT. */ + + kFRO_HF_to_CLKOUT = MUX_A(CM_CLKOUTCLKSEL, 3), /*!< Attach FRO_HF to CLKOUT. */ + + kFRO1M_to_CLKOUT = MUX_A(CM_CLKOUTCLKSEL, 4), /*!< Attach FRO1M to CLKOUT. */ + + kPLL1_to_CLKOUT = MUX_A(CM_CLKOUTCLKSEL, 5), /*!< Attach PLL1 to CLKOUT. */ + + kOSC32K_to_CLKOUT = MUX_A(CM_CLKOUTCLKSEL, 6), /*!< Attach OSC32K to CLKOUT. */ + + kNONE_to_SYS_CLKOUT = MUX_A(CM_CLKOUTCLKSEL, 7), /*!< Attach NONE to SYS_CLKOUT. */ + + kFRO12M_to_PLL0 = MUX_A(CM_PLL0CLKSEL, 0), /*!< Attach FRO12M to PLL0. */ + + kEXT_CLK_to_PLL0 = MUX_A(CM_PLL0CLKSEL, 1), /*!< Attach EXT_CLK to PLL0. */ + + kFRO1M_to_PLL0 = MUX_A(CM_PLL0CLKSEL, 2), /*!< Attach FRO1M to PLL0. */ + + kOSC32K_to_PLL0 = MUX_A(CM_PLL0CLKSEL, 3), /*!< Attach OSC32K to PLL0. */ + + kNONE_to_PLL0 = MUX_A(CM_PLL0CLKSEL, 7), /*!< Attach NONE to PLL0. */ + + kMCAN_DIV_to_MCAN = MUX_A(CM_MCANCLKSEL, 0), /*!< Attach MCAN_DIV to MCAN. */ + + kFRO1M_to_MCAN = MUX_A(CM_MCANCLKSEL, 1), /*!< Attach FRO1M to MCAN. */ + + kOSC32K_to_MCAN = MUX_A(CM_MCANCLKSEL, 2), /*!< Attach OSC32K to MCAN. */ + + kNONE_to_MCAN = MUX_A(CM_MCANCLKSEL, 7), /*!< Attach NONE to MCAN. */ + + kMAIN_CLK_to_ADC_CLK = MUX_A(CM_ADCASYNCCLKSEL, 0), /*!< Attach MAIN_CLK to ADC_CLK. */ + + kPLL0_to_ADC_CLK = MUX_A(CM_ADCASYNCCLKSEL, 1), /*!< Attach PLL0 to ADC_CLK. */ + + kFRO_HF_to_ADC_CLK = MUX_A(CM_ADCASYNCCLKSEL, 2), /*!< Attach FRO_HF to ADC_CLK. */ + + kEXT_CLK_to_ADC_CLK = MUX_A(CM_ADCASYNCCLKSEL, 4), /*!< Attach EXT_CLK to ADC_CLK. */ + + kNONE_to_ADC_CLK = MUX_A(CM_ADCASYNCCLKSEL, 7), /*!< Attach NONE to ADC_CLK. */ + + kMAIN_CLK_to_USB0_CLK = MUX_A(CM_USB0CLKSEL, 0), /*!< Attach MAIN_CLK to USB0_CLK. */ + + kPLL0_to_USB0_CLK = MUX_A(CM_USB0CLKSEL, 1), /*!< Attach PLL0 to USB0_CLK. */ + + kFRO_HF_to_USB0_CLK = MUX_A(CM_USB0CLKSEL, 3), /*!< Attach FRO_HF to USB0_CLK. */ + + kPLL1_to_USB0_CLK = MUX_A(CM_USB0CLKSEL, 5), /*!< Attach PLL1 to USB0_CLK. */ + + kNONE_to_USB0_CLK = MUX_A(CM_USB0CLKSEL, 7), /*!< Attach NONE to USB0_CLK. */ + + kOSC32K_to_CLK32K = MUX_A(CM_CLK32KCLKSEL, 0), /*!< Attach OSC32K to CLK32K. */ + + kFRO1MDIV_to_CLK32K = MUX_A(CM_CLK32KCLKSEL, 1), /*!< Attach FRO1MDIV to CLK32K. */ + + kNONE_to_CLK32K = MUX_A(CM_CLK32KCLKSEL, 7), /*!< Attach NONE to CLK32K. */ + + kMAIN_CLK_to_FLEXCOMM0 = MUX_A(CM_FXCOMCLKSEL0, 0), /*!< Attach MAIN_CLK to FLEXCOMM0. */ + + kPLL0_DIV_to_FLEXCOMM0 = MUX_A(CM_FXCOMCLKSEL0, 1), /*!< Attach PLL0_DIV to FLEXCOMM0. */ + + kFRO12M_to_FLEXCOMM0 = MUX_A(CM_FXCOMCLKSEL0, 2), /*!< Attach FRO12M to FLEXCOMM0. */ + + kFRO_HF_DIV_to_FLEXCOMM0 = MUX_A(CM_FXCOMCLKSEL0, 3), /*!< Attach FRO_HF_DIV to FLEXCOMM0. */ + + kFRO1M_to_FLEXCOMM0 = MUX_A(CM_FXCOMCLKSEL0, 4), /*!< Attach FRO1M to FLEXCOMM0. */ + + kMCLK_to_FLEXCOMM0 = MUX_A(CM_FXCOMCLKSEL0, 5), /*!< Attach MCLK to FLEXCOMM0. */ + + kOSC32K_to_FLEXCOMM0 = MUX_A(CM_FXCOMCLKSEL0, 6), /*!< Attach OSC32K to FLEXCOMM0. */ + + kNONE_to_FLEXCOMM0 = MUX_A(CM_FXCOMCLKSEL0, 7), /*!< Attach NONE to FLEXCOMM0. */ + + kMAIN_CLK_to_FLEXCOMM1 = MUX_A(CM_FXCOMCLKSEL1, 0), /*!< Attach MAIN_CLK to FLEXCOMM1. */ + + kPLL0_DIV_to_FLEXCOMM1 = MUX_A(CM_FXCOMCLKSEL1, 1), /*!< Attach PLL0_DIV to FLEXCOMM1. */ + + kFRO12M_to_FLEXCOMM1 = MUX_A(CM_FXCOMCLKSEL1, 2), /*!< Attach FRO12M to FLEXCOMM1. */ + + kFRO_HF_DIV_to_FLEXCOMM1 = MUX_A(CM_FXCOMCLKSEL1, 3), /*!< Attach FRO_HF_DIV to FLEXCOMM1. */ + + kFRO1M_to_FLEXCOMM1 = MUX_A(CM_FXCOMCLKSEL1, 4), /*!< Attach FRO1M to FLEXCOMM1. */ + + kMCLK_to_FLEXCOMM1 = MUX_A(CM_FXCOMCLKSEL1, 5), /*!< Attach MCLK to FLEXCOMM1. */ + + kOSC32K_to_FLEXCOMM1 = MUX_A(CM_FXCOMCLKSEL1, 6), /*!< Attach OSC32K to FLEXCOMM1. */ + + kNONE_to_FLEXCOMM1 = MUX_A(CM_FXCOMCLKSEL1, 7), /*!< Attach NONE to FLEXCOMM1. */ + + kMAIN_CLK_to_FLEXCOMM2 = MUX_A(CM_FXCOMCLKSEL2, 0), /*!< Attach MAIN_CLK to FLEXCOMM2. */ + + kPLL0_DIV_to_FLEXCOMM2 = MUX_A(CM_FXCOMCLKSEL2, 1), /*!< Attach PLL0_DIV to FLEXCOMM2. */ + + kFRO12M_to_FLEXCOMM2 = MUX_A(CM_FXCOMCLKSEL2, 2), /*!< Attach FRO12M to FLEXCOMM2. */ + + kFRO_HF_DIV_to_FLEXCOMM2 = MUX_A(CM_FXCOMCLKSEL2, 3), /*!< Attach FRO_HF_DIV to FLEXCOMM2. */ + + kFRO1M_to_FLEXCOMM2 = MUX_A(CM_FXCOMCLKSEL2, 4), /*!< Attach FRO1M to FLEXCOMM2. */ + + kMCLK_to_FLEXCOMM2 = MUX_A(CM_FXCOMCLKSEL2, 5), /*!< Attach MCLK to FLEXCOMM2. */ + + kOSC32K_to_FLEXCOMM2 = MUX_A(CM_FXCOMCLKSEL2, 6), /*!< Attach OSC32K to FLEXCOMM2. */ + + kNONE_to_FLEXCOMM2 = MUX_A(CM_FXCOMCLKSEL2, 7), /*!< Attach NONE to FLEXCOMM2. */ + + kMAIN_CLK_to_FLEXCOMM3 = MUX_A(CM_FXCOMCLKSEL3, 0), /*!< Attach MAIN_CLK to FLEXCOMM3. */ + + kPLL0_DIV_to_FLEXCOMM3 = MUX_A(CM_FXCOMCLKSEL3, 1), /*!< Attach PLL0_DIV to FLEXCOMM3. */ + + kFRO12M_to_FLEXCOMM3 = MUX_A(CM_FXCOMCLKSEL3, 2), /*!< Attach FRO12M to FLEXCOMM3. */ + + kFRO_HF_DIV_to_FLEXCOMM3 = MUX_A(CM_FXCOMCLKSEL3, 3), /*!< Attach FRO_HF_DIV to FLEXCOMM3. */ + + kFRO1M_to_FLEXCOMM3 = MUX_A(CM_FXCOMCLKSEL3, 4), /*!< Attach FRO1M to FLEXCOMM3. */ + + kMCLK_to_FLEXCOMM3 = MUX_A(CM_FXCOMCLKSEL3, 5), /*!< Attach MCLK to FLEXCOMM3. */ + + kOSC32K_to_FLEXCOMM3 = MUX_A(CM_FXCOMCLKSEL3, 6), /*!< Attach OSC32K to FLEXCOMM3. */ + + kNONE_to_FLEXCOMM3 = MUX_A(CM_FXCOMCLKSEL3, 7), /*!< Attach NONE to FLEXCOMM3. */ + + kMAIN_CLK_to_FLEXCOMM4 = MUX_A(CM_FXCOMCLKSEL4, 0), /*!< Attach MAIN_CLK to FLEXCOMM4. */ + + kPLL0_DIV_to_FLEXCOMM4 = MUX_A(CM_FXCOMCLKSEL4, 1), /*!< Attach PLL0_DIV to FLEXCOMM4. */ + + kFRO12M_to_FLEXCOMM4 = MUX_A(CM_FXCOMCLKSEL4, 2), /*!< Attach FRO12M to FLEXCOMM4. */ + + kFRO_HF_DIV_to_FLEXCOMM4 = MUX_A(CM_FXCOMCLKSEL4, 3), /*!< Attach FRO_HF_DIV to FLEXCOMM4. */ + + kFRO1M_to_FLEXCOMM4 = MUX_A(CM_FXCOMCLKSEL4, 4), /*!< Attach FRO1M to FLEXCOMM4. */ + + kMCLK_to_FLEXCOMM4 = MUX_A(CM_FXCOMCLKSEL4, 5), /*!< Attach MCLK to FLEXCOMM4. */ + + kOSC32K_to_FLEXCOMM4 = MUX_A(CM_FXCOMCLKSEL4, 6), /*!< Attach OSC32K to FLEXCOMM4. */ + + kNONE_to_FLEXCOMM4 = MUX_A(CM_FXCOMCLKSEL4, 7), /*!< Attach NONE to FLEXCOMM4. */ + + kMAIN_CLK_to_FLEXCOMM5 = MUX_A(CM_FXCOMCLKSEL5, 0), /*!< Attach MAIN_CLK to FLEXCOMM5. */ + + kPLL0_DIV_to_FLEXCOMM5 = MUX_A(CM_FXCOMCLKSEL5, 1), /*!< Attach PLL0_DIV to FLEXCOMM5. */ + + kFRO12M_to_FLEXCOMM5 = MUX_A(CM_FXCOMCLKSEL5, 2), /*!< Attach FRO12M to FLEXCOMM5. */ + + kFRO_HF_DIV_to_FLEXCOMM5 = MUX_A(CM_FXCOMCLKSEL5, 3), /*!< Attach FRO_HF_DIV to FLEXCOMM5. */ + + kFRO1M_to_FLEXCOMM5 = MUX_A(CM_FXCOMCLKSEL5, 4), /*!< Attach FRO1M to FLEXCOMM5. */ + + kMCLK_to_FLEXCOMM5 = MUX_A(CM_FXCOMCLKSEL5, 5), /*!< Attach MCLK to FLEXCOMM5. */ + + kOSC32K_to_FLEXCOMM5 = MUX_A(CM_FXCOMCLKSEL5, 6), /*!< Attach OSC32K to FLEXCOMM5. */ + + kNONE_to_FLEXCOMM5 = MUX_A(CM_FXCOMCLKSEL5, 7), /*!< Attach NONE to FLEXCOMM5. */ + + kMAIN_CLK_to_FLEXCOMM6 = MUX_A(CM_FXCOMCLKSEL6, 0), /*!< Attach MAIN_CLK to FLEXCOMM6. */ + + kPLL0_DIV_to_FLEXCOMM6 = MUX_A(CM_FXCOMCLKSEL6, 1), /*!< Attach PLL0_DIV to FLEXCOMM6. */ + + kFRO12M_to_FLEXCOMM6 = MUX_A(CM_FXCOMCLKSEL6, 2), /*!< Attach FRO12M to FLEXCOMM6. */ + + kFRO_HF_DIV_to_FLEXCOMM6 = MUX_A(CM_FXCOMCLKSEL6, 3), /*!< Attach FRO_HF_DIV to FLEXCOMM6. */ + + kFRO1M_to_FLEXCOMM6 = MUX_A(CM_FXCOMCLKSEL6, 4), /*!< Attach FRO1M to FLEXCOMM6. */ + + kMCLK_to_FLEXCOMM6 = MUX_A(CM_FXCOMCLKSEL6, 5), /*!< Attach MCLK to FLEXCOMM6. */ + + kOSC32K_to_FLEXCOMM6 = MUX_A(CM_FXCOMCLKSEL6, 6), /*!< Attach OSC32K to FLEXCOMM6. */ + + kNONE_to_FLEXCOMM6 = MUX_A(CM_FXCOMCLKSEL6, 7), /*!< Attach NONE to FLEXCOMM6. */ + + kMAIN_CLK_to_FLEXCOMM7 = MUX_A(CM_FXCOMCLKSEL7, 0), /*!< Attach MAIN_CLK to FLEXCOMM7. */ + + kPLL0_DIV_to_FLEXCOMM7 = MUX_A(CM_FXCOMCLKSEL7, 1), /*!< Attach PLL0_DIV to FLEXCOMM7. */ + + kFRO12M_to_FLEXCOMM7 = MUX_A(CM_FXCOMCLKSEL7, 2), /*!< Attach FRO12M to FLEXCOMM7. */ + + kFRO_HF_DIV_to_FLEXCOMM7 = MUX_A(CM_FXCOMCLKSEL7, 3), /*!< Attach FRO_HF_DIV to FLEXCOMM7. */ + + kFRO1M_to_FLEXCOMM7 = MUX_A(CM_FXCOMCLKSEL7, 4), /*!< Attach FRO1M to FLEXCOMM7. */ + + kMCLK_to_FLEXCOMM7 = MUX_A(CM_FXCOMCLKSEL7, 5), /*!< Attach MCLK to FLEXCOMM7. */ + + kOSC32K_to_FLEXCOMM7 = MUX_A(CM_FXCOMCLKSEL7, 6), /*!< Attach OSC32K to FLEXCOMM7. */ + + kNONE_to_FLEXCOMM7 = MUX_A(CM_FXCOMCLKSEL7, 7), /*!< Attach NONE to FLEXCOMM7. */ + + kMAIN_CLK_to_HSLSPI = MUX_A(CM_HSLSPICLKSEL, 0), /*!< Attach MAIN_CLK to HSLSPI. */ + + kPLL0_DIV_to_HSLSPI = MUX_A(CM_HSLSPICLKSEL, 1), /*!< Attach PLL0_DIV to HSLSPI. */ + + kFRO12M_to_HSLSPI = MUX_A(CM_HSLSPICLKSEL, 2), /*!< Attach FRO12M to HSLSPI. */ + + kFRO_HF_DIV_to_HSLSPI = MUX_A(CM_HSLSPICLKSEL, 3), /*!< Attach FRO_HF_DIV to HSLSPI. */ + + kFRO1M_to_HSLSPI = MUX_A(CM_HSLSPICLKSEL, 4), /*!< Attach FRO1M to HSLSPI. */ + + kOSC32K_to_HSLSPI = MUX_A(CM_HSLSPICLKSEL, 6), /*!< Attach OSC32K to HSLSPI. */ + + kNONE_to_HSLSPI = MUX_A(CM_HSLSPICLKSEL, 7), /*!< Attach NONE to HSLSPI. */ + + kFRO_HF_to_MCLK = MUX_A(CM_MCLKCLKSEL, 0), /*!< Attach FRO_HF to MCLK. */ + + kPLL0_to_MCLK = MUX_A(CM_MCLKCLKSEL, 1), /*!< Attach PLL0 to MCLK. */ + + kNONE_to_MCLK = MUX_A(CM_MCLKCLKSEL, 7), /*!< Attach NONE to MCLK. */ + + kMAIN_CLK_to_SCT_CLK = MUX_A(CM_SCTCLKSEL, 0), /*!< Attach MAIN_CLK to SCT_CLK. */ + + kPLL0_to_SCT_CLK = MUX_A(CM_SCTCLKSEL, 1), /*!< Attach PLL0 to SCT_CLK. */ + + kEXT_CLK_to_SCT_CLK = MUX_A(CM_SCTCLKSEL, 2), /*!< Attach EXT_CLK to SCT_CLK. */ + + kFRO_HF_to_SCT_CLK = MUX_A(CM_SCTCLKSEL, 3), /*!< Attach FRO_HF to SCT_CLK. */ + + kMCLK_to_SCT_CLK = MUX_A(CM_SCTCLKSEL, 5), /*!< Attach MCLK to SCT_CLK. */ + + kNONE_to_SCT_CLK = MUX_A(CM_SCTCLKSEL, 7), /*!< Attach NONE to SCT_CLK. */ + + kFRO32K_to_OSC32K = MUX_A(CM_RTCOSC32KCLKSEL, 0), /*!< Attach FRO32K to OSC32K. */ + + kXTAL32K_to_OSC32K = MUX_A(CM_RTCOSC32KCLKSEL, 1), /*!< Attach XTAL32K to OSC32K. */ + + kOSC32K_to_OSTIMER = MUX_A(CM_OSTIMERCLKSEL, 0), /*!< Attach OSC32K to OSTIMER. */ + + kFRO1M_to_OSTIMER = MUX_A(CM_OSTIMERCLKSEL, 1), /*!< Attach FRO1M to OSTIMER. */ + + kMAIN_CLK_to_OSTIMER = MUX_A(CM_OSTIMERCLKSEL, 2), /*!< Attach MAIN_CLK to OSTIMER. */ + + kTRACE_DIV_to_TRACE = MUX_A(CM_TRACECLKSEL, 0), /*!< Attach TRACE_DIV to TRACE. */ + + kFRO1M_to_TRACE = MUX_A(CM_TRACECLKSEL, 1), /*!< Attach FRO1M to TRACE. */ + + kOSC32K_to_TRACE = MUX_A(CM_TRACECLKSEL, 2), /*!< Attach OSC32K to TRACE. */ + + kNONE_to_TRACE = MUX_A(CM_TRACECLKSEL, 7), /*!< Attach NONE to TRACE. */ + + kSYSTICK_DIV0_to_SYSTICK0 = MUX_A(CM_SYSTICKCLKSEL0, 0), /*!< Attach SYSTICK_DIV0 to SYSTICK0. */ + + kFRO1M_to_SYSTICK0 = MUX_A(CM_SYSTICKCLKSEL0, 1), /*!< Attach FRO1M to SYSTICK0. */ + + kOSC32K_to_SYSTICK0 = MUX_A(CM_SYSTICKCLKSEL0, 2), /*!< Attach OSC32K to SYSTICK0. */ + + kNONE_to_SYSTICK0 = MUX_A(CM_SYSTICKCLKSEL0, 7), /*!< Attach NONE to SYSTICK0. */ + + kFRO12M_to_PLL1 = MUX_A(CM_PLL1CLKSEL, 0), /*!< Attach FRO12M to PLL1. */ + + kEXT_CLK_to_PLL1 = MUX_A(CM_PLL1CLKSEL, 1), /*!< Attach EXT_CLK to PLL1. */ + + kFRO1M_to_PLL1 = MUX_A(CM_PLL1CLKSEL, 2), /*!< Attach FRO1M to PLL1. */ + + kOSC32K_to_PLL1 = MUX_A(CM_PLL1CLKSEL, 3), /*!< Attach OSC32K to PLL1. */ + + kNONE_to_PLL1 = MUX_A(CM_PLL1CLKSEL, 7), /*!< Attach NONE to PLL1. */ + + kMAIN_CLK_to_CTIMER0 = MUX_A(CM_CTIMERCLKSEL0, 0), /*!< Attach MAIN_CLK to CTIMER0. */ + + kPLL0_to_CTIMER0 = MUX_A(CM_CTIMERCLKSEL0, 1), /*!< Attach PLL0 to CTIMER0. */ + + kFRO_HF_to_CTIMER0 = MUX_A(CM_CTIMERCLKSEL0, 3), /*!< Attach FRO_HF to CTIMER0. */ + + kFRO1M_to_CTIMER0 = MUX_A(CM_CTIMERCLKSEL0, 4), /*!< Attach FRO1M to CTIMER0. */ + + kMCLK_to_CTIMER0 = MUX_A(CM_CTIMERCLKSEL0, 5), /*!< Attach MCLK to CTIMER0. */ + + kOSC32K_to_CTIMER0 = MUX_A(CM_CTIMERCLKSEL0, 6), /*!< Attach OSC32K to CTIMER0. */ + + kNONE_to_CTIMER0 = MUX_A(CM_CTIMERCLKSEL0, 7), /*!< Attach NONE to CTIMER0. */ + + kMAIN_CLK_to_CTIMER1 = MUX_A(CM_CTIMERCLKSEL1, 0), /*!< Attach MAIN_CLK to CTIMER1. */ + + kPLL0_to_CTIMER1 = MUX_A(CM_CTIMERCLKSEL1, 1), /*!< Attach PLL0 to CTIMER1. */ + + kFRO_HF_to_CTIMER1 = MUX_A(CM_CTIMERCLKSEL1, 3), /*!< Attach FRO_HF to CTIMER1. */ + + kFRO1M_to_CTIMER1 = MUX_A(CM_CTIMERCLKSEL1, 4), /*!< Attach FRO1M to CTIMER1. */ + + kMCLK_to_CTIMER1 = MUX_A(CM_CTIMERCLKSEL1, 5), /*!< Attach MCLK to CTIMER1. */ + + kOSC32K_to_CTIMER1 = MUX_A(CM_CTIMERCLKSEL1, 6), /*!< Attach OSC32K to CTIMER1. */ + + kNONE_to_CTIMER1 = MUX_A(CM_CTIMERCLKSEL1, 7), /*!< Attach NONE to CTIMER1. */ + + kMAIN_CLK_to_CTIMER2 = MUX_A(CM_CTIMERCLKSEL2, 0), /*!< Attach MAIN_CLK to CTIMER2. */ + + kPLL0_to_CTIMER2 = MUX_A(CM_CTIMERCLKSEL2, 1), /*!< Attach PLL0 to CTIMER2. */ + + kFRO_HF_to_CTIMER2 = MUX_A(CM_CTIMERCLKSEL2, 3), /*!< Attach FRO_HF to CTIMER2. */ + + kFRO1M_to_CTIMER2 = MUX_A(CM_CTIMERCLKSEL2, 4), /*!< Attach FRO1M to CTIMER2. */ + + kMCLK_to_CTIMER2 = MUX_A(CM_CTIMERCLKSEL2, 5), /*!< Attach MCLK to CTIMER2. */ + + kOSC32K_to_CTIMER2 = MUX_A(CM_CTIMERCLKSEL2, 6), /*!< Attach OSC32K to CTIMER2. */ + + kNONE_to_CTIMER2 = MUX_A(CM_CTIMERCLKSEL2, 7), /*!< Attach NONE to CTIMER2. */ + + kMAIN_CLK_to_CTIMER3 = MUX_A(CM_CTIMERCLKSEL3, 0), /*!< Attach MAIN_CLK to CTIMER3. */ + + kPLL0_to_CTIMER3 = MUX_A(CM_CTIMERCLKSEL3, 1), /*!< Attach PLL0 to CTIMER3. */ + + kFRO_HF_to_CTIMER3 = MUX_A(CM_CTIMERCLKSEL3, 3), /*!< Attach FRO_HF to CTIMER3. */ + + kFRO1M_to_CTIMER3 = MUX_A(CM_CTIMERCLKSEL3, 4), /*!< Attach FRO1M to CTIMER3. */ + + kMCLK_to_CTIMER3 = MUX_A(CM_CTIMERCLKSEL3, 5), /*!< Attach MCLK to CTIMER3. */ + + kOSC32K_to_CTIMER3 = MUX_A(CM_CTIMERCLKSEL3, 6), /*!< Attach OSC32K to CTIMER3. */ + + kNONE_to_CTIMER3 = MUX_A(CM_CTIMERCLKSEL3, 7), /*!< Attach NONE to CTIMER3. */ + + kMAIN_CLK_to_CTIMER4 = MUX_A(CM_CTIMERCLKSEL4, 0), /*!< Attach MAIN_CLK to CTIMER4. */ + + kPLL0_to_CTIMER4 = MUX_A(CM_CTIMERCLKSEL4, 1), /*!< Attach PLL0 to CTIMER4. */ + + kFRO_HF_to_CTIMER4 = MUX_A(CM_CTIMERCLKSEL4, 3), /*!< Attach FRO_HF to CTIMER4. */ + + kFRO1M_to_CTIMER4 = MUX_A(CM_CTIMERCLKSEL4, 4), /*!< Attach FRO1M to CTIMER4. */ + + kMCLK_to_CTIMER4 = MUX_A(CM_CTIMERCLKSEL4, 5), /*!< Attach MCLK to CTIMER4. */ + + kOSC32K_to_CTIMER4 = MUX_A(CM_CTIMERCLKSEL4, 6), /*!< Attach OSC32K to CTIMER4. */ + + kNONE_to_CTIMER4 = MUX_A(CM_CTIMERCLKSEL4, 7), /*!< Attach NONE to CTIMER4. */ + + kNONE_to_NONE = (int)0x80000000U, /*!< Attach NONE to NONE. */ + +} clock_attach_id_t; + +/*! @brief Clock dividers */ +typedef enum _clock_div_name +{ + kCLOCK_DivSystickClk0 = 0, /*!< Systick Clk0 Divider. */ + + kCLOCK_DivArmTrClkDiv = 2, /*!< Arm Tr Clk Div Divider. */ + + kCLOCK_DivCanClk = 3, /*!< Can Clock Divider. */ + + kCLOCK_DivFlexFrg0 = 8, /*!< Flex Frg0 Divider. */ + + kCLOCK_DivFlexFrg1 = 9, /*!< Flex Frg1 Divider. */ + + kCLOCK_DivFlexFrg2 = 10, /*!< Flex Frg2 Divider. */ + + kCLOCK_DivFlexFrg3 = 11, /*!< Flex Frg3 Divider. */ + + kCLOCK_DivFlexFrg4 = 12, /*!< Flex Frg4 Divider. */ + + kCLOCK_DivFlexFrg5 = 13, /*!< Flex Frg5 Divider. */ + + kCLOCK_DivFlexFrg6 = 14, /*!< Flex Frg6 Divider. */ + + kCLOCK_DivFlexFrg7 = 15, /*!< Flex Frg7 Divider. */ + + kCLOCK_DivAhbClk = 32, /*!< Ahb Clock Divider. */ + + kCLOCK_DivClkOut = 33, /*!< Clk Out Divider. */ + + kCLOCK_DivFrohfClk = 34, /*!< Frohf Clock Divider. */ + + kCLOCK_DivWdtClk = 35, /*!< Wdt Clock Divider. */ + + kCLOCK_DivAdcAsyncClk = 37, /*!< Adc Async Clock Divider. */ + + kCLOCK_DivUsb0Clk = 38, /*!< Usb0 Clock Divider. */ + + kCLOCK_DivFro1mClk = 40, /*!< Fro1m Clock Divider. */ + + kCLOCK_DivMClk = 43, /*!< I2S MCLK Clock Divider. */ + + kCLOCK_DivSctClk = 45, /*!< Sct Clock Divider. */ + + kCLOCK_DivPll0Clk = 49 /*!< PLL clock divider. */ +} clock_div_name_t; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Enable the clock for specific IP. + * @param clk : Clock to be enabled. + * @return Nothing + */ +static inline void CLOCK_EnableClock(clock_ip_name_t clk) +{ + uint32_t index = CLK_GATE_ABSTRACT_REG_OFFSET(clk); + SYSCON->AHBCLKCTRLSET[index] = (1UL << CLK_GATE_ABSTRACT_BITS_SHIFT(clk)); +} +/** + * @brief Disable the clock for specific IP. + * @param clk : Clock to be Disabled. + * @return Nothing + */ +static inline void CLOCK_DisableClock(clock_ip_name_t clk) +{ + uint32_t index = CLK_GATE_ABSTRACT_REG_OFFSET(clk); + SYSCON->AHBCLKCTRLCLR[index] = (1UL << CLK_GATE_ABSTRACT_BITS_SHIFT(clk)); +} +/** + * @brief Initialize the Core clock to given frequency (12, 48 or 96 MHz). + * Turns on FRO and uses default CCO, if freq is 12000000, then high speed output is off, else high speed output is + * enabled. + * @param iFreq : Desired frequency (must be one of CLK_FRO_12MHZ or CLK_FRO_48MHZ or CLK_FRO_96MHZ) + * @return returns success or fail status. + */ +status_t CLOCK_SetupFROClocking(uint32_t iFreq); +/** + * @brief Set the flash wait states for the input freuqency. + * @param system_freq_hz : Input frequency + * @return Nothing + */ +void CLOCK_SetFLASHAccessCyclesForFreq(uint32_t system_freq_hz); +/** + * @brief Initialize the external osc clock to given frequency. + * @param iFreq : Desired frequency (must be equal to exact rate in Hz) + * @return returns success or fail status. + */ +status_t CLOCK_SetupExtClocking(uint32_t iFreq); +/** + * @brief Initialize the I2S MCLK clock to given frequency. + * @param iFreq : Desired frequency (must be equal to exact rate in Hz) + * @return returns success or fail status. + */ +status_t CLOCK_SetupI2SMClkClocking(uint32_t iFreq); +/** + * @brief Initialize the PLU CLKIN clock to given frequency. + * @param iFreq : Desired frequency (must be equal to exact rate in Hz) + * @return returns success or fail status. + */ +status_t CLOCK_SetupPLUClkInClocking(uint32_t iFreq); +/** + * @brief Configure the clock selection muxes. + * @param connection : Clock to be configured. + * @return Nothing + */ +void CLOCK_AttachClk(clock_attach_id_t connection); +/** + * @brief Get the actual clock attach id. + * This fuction uses the offset in input attach id, then it reads the actual source value in + * the register and combine the offset to obtain an actual attach id. + * @param attachId : Clock attach id to get. + * @return Clock source value. + */ +clock_attach_id_t CLOCK_GetClockAttachId(clock_attach_id_t attachId); +/** + * @brief Setup peripheral clock dividers. + * @param div_name : Clock divider name + * @param divided_by_value: Value to be divided + * @param reset : Whether to reset the divider counter. + * @return Nothing + */ +void CLOCK_SetClkDiv(clock_div_name_t div_name, uint32_t divided_by_value, bool reset); +/** + * @brief Setup rtc 1khz clock divider. + * @param divided_by_value: Value to be divided + * @return Nothing + */ +void CLOCK_SetRtc1khzClkDiv(uint32_t divided_by_value); +/** + * @brief Setup rtc 1hz clock divider. + * @param divided_by_value: Value to be divided + * @return Nothing + */ +void CLOCK_SetRtc1hzClkDiv(uint32_t divided_by_value); + +/** + * @brief Set the flexcomm output frequency. + * @param id : flexcomm instance id + * @param freq : output frequency + * @return 0 : the frequency range is out of range. + * 1 : switch successfully. + */ +uint32_t CLOCK_SetFlexCommClock(uint32_t id, uint32_t freq); + +/*! @brief Return Frequency of flexcomm input clock + * @param id : flexcomm instance id + * @return Frequency value + */ +uint32_t CLOCK_GetFlexCommInputClock(uint32_t id); + +/*! @brief Return Frequency of selected clock + * @return Frequency of selected clock + */ +uint32_t CLOCK_GetFreq(clock_name_t clockName); +/*! @brief Return Frequency of FRO 12MHz + * @return Frequency of FRO 12MHz + */ +uint32_t CLOCK_GetFro12MFreq(void); +/*! @brief Return Frequency of FRO 1MHz + * @return Frequency of FRO 1MHz + */ +uint32_t CLOCK_GetFro1MFreq(void); +/*! @brief Return Frequency of ClockOut + * @return Frequency of ClockOut + */ +uint32_t CLOCK_GetClockOutClkFreq(void); +/*! @brief Return Frequency of Can Clock + * @return Frequency of Can. + */ +uint32_t CLOCK_GetMCanClkFreq(void); +/*! @brief Return Frequency of Adc Clock + * @return Frequency of Adc. + */ +uint32_t CLOCK_GetAdcClkFreq(void); +/*! @brief Return Frequency of Usb0 Clock + * @return Frequency of Usb0 Clock. + */ +uint32_t CLOCK_GetUsb0ClkFreq(void); +/*! @brief Return Frequency of Usb1 Clock + * @return Frequency of Usb1 Clock. + */ +uint32_t CLOCK_GetUsb1ClkFreq(void); +/*! @brief Return Frequency of MClk Clock + * @return Frequency of MClk Clock. + */ +uint32_t CLOCK_GetMclkClkFreq(void); +/*! @brief Return Frequency of SCTimer Clock + * @return Frequency of SCTimer Clock. + */ +uint32_t CLOCK_GetSctClkFreq(void); +/*! @brief Return Frequency of External Clock + * @return Frequency of External Clock. If no external clock is used returns 0. + */ +uint32_t CLOCK_GetExtClkFreq(void); +/*! @brief Return Frequency of Watchdog + * @return Frequency of Watchdog + */ +uint32_t CLOCK_GetWdtClkFreq(void); +/*! @brief Return Frequency of High-Freq output of FRO + * @return Frequency of High-Freq output of FRO + */ +uint32_t CLOCK_GetFroHfFreq(void); +/*! @brief Return Frequency of PLL + * @return Frequency of PLL + */ +uint32_t CLOCK_GetPll0OutFreq(void); +/*! @brief Return Frequency of USB PLL + * @return Frequency of PLL + */ +uint32_t CLOCK_GetPll1OutFreq(void); +/*! @brief Return Frequency of 32kHz osc + * @return Frequency of 32kHz osc + */ +uint32_t CLOCK_GetOsc32KFreq(void); +/*! @brief Return Frequency of Core System + * @return Frequency of Core System + */ +uint32_t CLOCK_GetCoreSysClkFreq(void); +/*! @brief Return Frequency of I2S MCLK Clock + * @return Frequency of I2S MCLK Clock + */ +uint32_t CLOCK_GetI2SMClkFreq(void); +/*! @brief Return Frequency of PLU CLKIN Clock + * @return Frequency of PLU CLKIN Clock + */ +uint32_t CLOCK_GetPLUClkInFreq(void); +/*! @brief Return Frequency of FlexComm Clock + * @return Frequency of FlexComm Clock + */ +uint32_t CLOCK_GetFlexCommClkFreq(uint32_t id); +/*! @brief Return Frequency of High speed SPI Clock + * @return Frequency of High speed SPI Clock + */ +uint32_t CLOCK_GetHsLspiClkFreq(void); +/*! @brief Return Frequency of CTimer functional Clock + * @return Frequency of CTimer functional Clock + */ +uint32_t CLOCK_GetCTimerClkFreq(uint32_t id); +/*! @brief Return Frequency of SystickClock + * @return Frequency of Systick Clock + */ +uint32_t CLOCK_GetSystickClkFreq(uint32_t id); + +/*! @brief Return PLL0 input clock rate + * @return PLL0 input clock rate + */ +uint32_t CLOCK_GetPLL0InClockRate(void); + +/*! @brief Return PLL1 input clock rate + * @return PLL1 input clock rate + */ +uint32_t CLOCK_GetPLL1InClockRate(void); + +/*! @brief Return PLL0 output clock rate + * @param recompute : Forces a PLL rate recomputation if true + * @return PLL0 output clock rate + * @note The PLL rate is cached in the driver in a variable as + * the rate computation function can take some time to perform. It + * is recommended to use 'false' with the 'recompute' parameter. + */ +uint32_t CLOCK_GetPLL0OutClockRate(bool recompute); + +/*! @brief Return PLL1 output clock rate + * @param recompute : Forces a PLL rate recomputation if true + * @return PLL1 output clock rate + * @note The PLL rate is cached in the driver in a variable as + * the rate computation function can take some time to perform. It + * is recommended to use 'false' with the 'recompute' parameter. + */ +uint32_t CLOCK_GetPLL1OutClockRate(bool recompute); + +/*! @brief Enables and disables PLL0 bypass mode + * @brief bypass : true to bypass PLL0 (PLL0 output = PLL0 input, false to disable bypass + * @return PLL0 output clock rate + */ +__STATIC_INLINE void CLOCK_SetBypassPLL0(bool bypass) +{ + if (bypass) + { + SYSCON->PLL0CTRL |= (1UL << SYSCON_PLL0CTRL_BYPASSPLL_SHIFT); + } + else + { + SYSCON->PLL0CTRL &= ~(1UL << SYSCON_PLL0CTRL_BYPASSPLL_SHIFT); + } +} + +/*! @brief Enables and disables PLL1 bypass mode + * @brief bypass : true to bypass PLL1 (PLL1 output = PLL1 input, false to disable bypass + * @return PLL1 output clock rate + */ +__STATIC_INLINE void CLOCK_SetBypassPLL1(bool bypass) +{ + if (bypass) + { + SYSCON->PLL1CTRL |= (1UL << SYSCON_PLL1CTRL_BYPASSPLL_SHIFT); + } + else + { + SYSCON->PLL1CTRL &= ~(1UL << SYSCON_PLL1CTRL_BYPASSPLL_SHIFT); + } +} + +/*! @brief Check if PLL is locked or not + * @return true if the PLL is locked, false if not locked + */ +__STATIC_INLINE bool CLOCK_IsPLL0Locked(void) +{ + return (bool)((SYSCON->PLL0STAT & SYSCON_PLL0STAT_LOCK_MASK) != 0UL); +} + +/*! @brief Check if PLL1 is locked or not + * @return true if the PLL1 is locked, false if not locked + */ +__STATIC_INLINE bool CLOCK_IsPLL1Locked(void) +{ + return (bool)((SYSCON->PLL1STAT & SYSCON_PLL1STAT_LOCK_MASK) != 0UL); +} + +/*! @brief Store the current PLL0 rate + * @param rate: Current rate of the PLL0 + * @return Nothing + **/ +void CLOCK_SetStoredPLL0ClockRate(uint32_t rate); + +/*! @brief PLL configuration structure flags for 'flags' field + * These flags control how the PLL configuration function sets up the PLL setup structure.
+ * + * When the PLL_CONFIGFLAG_USEINRATE flag is selected, the 'InputRate' field in the + * configuration structure must be assigned with the expected PLL frequency. If the + * PLL_CONFIGFLAG_USEINRATE is not used, 'InputRate' is ignored in the configuration + * function and the driver will determine the PLL rate from the currently selected + * PLL source. This flag might be used to configure the PLL input clock more accurately + * when using the WDT oscillator or a more dyanmic CLKIN source.
+ * + * When the PLL_CONFIGFLAG_FORCENOFRACT flag is selected, the PLL hardware for the + * automatic bandwidth selection, Spread Spectrum (SS) support, and fractional M-divider + * are not used.
+ */ +#define PLL_CONFIGFLAG_USEINRATE (1U << 0U) /*!< Flag to use InputRate in PLL configuration structure for setup */ +#define PLL_CONFIGFLAG_FORCENOFRACT (1U << 2U) +/*!< Force non-fractional output mode, PLL output will not use the fractional, automatic bandwidth, or SS hardware */ + +/*! @brief PLL Spread Spectrum (SS) Programmable modulation frequency + * See (MF) field in the PLL0SSCG1 register in the UM. + */ +typedef enum _ss_progmodfm +{ + kSS_MF_512 = (0 << 20), /*!< Nss = 512 (fm ? 3.9 - 7.8 kHz) */ + kSS_MF_384 = (1 << 20), /*!< Nss ?= 384 (fm ? 5.2 - 10.4 kHz) */ + kSS_MF_256 = (2 << 20), /*!< Nss = 256 (fm ? 7.8 - 15.6 kHz) */ + kSS_MF_128 = (3 << 20), /*!< Nss = 128 (fm ? 15.6 - 31.3 kHz) */ + kSS_MF_64 = (4 << 20), /*!< Nss = 64 (fm ? 32.3 - 64.5 kHz) */ + kSS_MF_32 = (5 << 20), /*!< Nss = 32 (fm ? 62.5- 125 kHz) */ + kSS_MF_24 = (6 << 20), /*!< Nss ?= 24 (fm ? 83.3- 166.6 kHz) */ + kSS_MF_16 = (7 << 20) /*!< Nss = 16 (fm ? 125- 250 kHz) */ +} ss_progmodfm_t; + +/*! @brief PLL Spread Spectrum (SS) Programmable frequency modulation depth + * See (MR) field in the PLL0SSCG1 register in the UM. + */ +typedef enum _ss_progmoddp +{ + kSS_MR_K0 = (0 << 23), /*!< k = 0 (no spread spectrum) */ + kSS_MR_K1 = (1 << 23), /*!< k = 1 */ + kSS_MR_K1_5 = (2 << 23), /*!< k = 1.5 */ + kSS_MR_K2 = (3 << 23), /*!< k = 2 */ + kSS_MR_K3 = (4 << 23), /*!< k = 3 */ + kSS_MR_K4 = (5 << 23), /*!< k = 4 */ + kSS_MR_K6 = (6 << 23), /*!< k = 6 */ + kSS_MR_K8 = (7 << 23) /*!< k = 8 */ +} ss_progmoddp_t; + +/*! @brief PLL Spread Spectrum (SS) Modulation waveform control + * See (MC) field in the PLL0SSCG1 register in the UM.
+ * Compensation for low pass filtering of the PLL to get a triangular + * modulation at the output of the PLL, giving a flat frequency spectrum. + */ +typedef enum _ss_modwvctrl +{ + kSS_MC_NOC = (0 << 26), /*!< no compensation */ + kSS_MC_RECC = (2 << 26), /*!< recommended setting */ + kSS_MC_MAXC = (3 << 26), /*!< max. compensation */ +} ss_modwvctrl_t; + +/*! @brief PLL configuration structure + * + * This structure can be used to configure the settings for a PLL + * setup structure. Fill in the desired configuration for the PLL + * and call the PLL setup function to fill in a PLL setup structure. + */ +typedef struct _pll_config +{ + uint32_t desiredRate; /*!< Desired PLL rate in Hz */ + uint32_t inputRate; /*!< PLL input clock in Hz, only used if PLL_CONFIGFLAG_USEINRATE flag is set */ + uint32_t flags; /*!< PLL configuration flags, Or'ed value of PLL_CONFIGFLAG_* definitions */ + ss_progmodfm_t ss_mf; /*!< SS Programmable modulation frequency, only applicable when not using + PLL_CONFIGFLAG_FORCENOFRACT flag */ + ss_progmoddp_t ss_mr; /*!< SS Programmable frequency modulation depth, only applicable when not using + PLL_CONFIGFLAG_FORCENOFRACT flag */ + ss_modwvctrl_t + ss_mc; /*!< SS Modulation waveform control, only applicable when not using PLL_CONFIGFLAG_FORCENOFRACT flag */ + bool mfDither; /*!< false for fixed modulation frequency or true for dithering, only applicable when not using + PLL_CONFIGFLAG_FORCENOFRACT flag */ + +} pll_config_t; + +/*! @brief PLL setup structure flags for 'flags' field + * These flags control how the PLL setup function sets up the PLL + */ +#define PLL_SETUPFLAG_POWERUP (1U << 0U) /*!< Setup will power on the PLL after setup */ +#define PLL_SETUPFLAG_WAITLOCK (1U << 1U) /*!< Setup will wait for PLL lock, implies the PLL will be pwoered on */ +#define PLL_SETUPFLAG_ADGVOLT (1U << 2U) /*!< Optimize system voltage for the new PLL rate */ +#define PLL_SETUPFLAG_USEFEEDBACKDIV2 (1U << 3U) /*!< Use feedback divider by 2 in divider path */ + +/*! @brief PLL0 setup structure + * This structure can be used to pre-build a PLL setup configuration + * at run-time and quickly set the PLL to the configuration. It can be + * populated with the PLL setup function. If powering up or waiting + * for PLL lock, the PLL input clock source should be configured prior + * to PLL setup. + */ +typedef struct _pll_setup +{ + uint32_t pllctrl; /*!< PLL control register PLL0CTRL */ + uint32_t pllndec; /*!< PLL NDEC register PLL0NDEC */ + uint32_t pllpdec; /*!< PLL PDEC register PLL0PDEC */ + uint32_t pllmdec; /*!< PLL MDEC registers PLL0PDEC */ + uint32_t pllsscg[2]; /*!< PLL SSCTL registers PLL0SSCG*/ + uint32_t pllRate; /*!< Acutal PLL rate */ + uint32_t flags; /*!< PLL setup flags, Or'ed value of PLL_SETUPFLAG_* definitions */ +} pll_setup_t; + +/*! @brief PLL status definitions + */ +typedef enum _pll_error +{ + kStatus_PLL_Success = MAKE_STATUS(kStatusGroup_Generic, 0), /*!< PLL operation was successful */ + kStatus_PLL_OutputTooLow = MAKE_STATUS(kStatusGroup_Generic, 1), /*!< PLL output rate request was too low */ + kStatus_PLL_OutputTooHigh = MAKE_STATUS(kStatusGroup_Generic, 2), /*!< PLL output rate request was too high */ + kStatus_PLL_InputTooLow = MAKE_STATUS(kStatusGroup_Generic, 3), /*!< PLL input rate is too low */ + kStatus_PLL_InputTooHigh = MAKE_STATUS(kStatusGroup_Generic, 4), /*!< PLL input rate is too high */ + kStatus_PLL_OutsideIntLimit = MAKE_STATUS(kStatusGroup_Generic, 5), /*!< Requested output rate isn't possible */ + kStatus_PLL_CCOTooLow = MAKE_STATUS(kStatusGroup_Generic, 6), /*!< Requested CCO rate isn't possible */ + kStatus_PLL_CCOTooHigh = MAKE_STATUS(kStatusGroup_Generic, 7) /*!< Requested CCO rate isn't possible */ +} pll_error_t; + +/*! @brief USB FS clock source definition. */ +typedef enum _clock_usbfs_src +{ + kCLOCK_UsbfsSrcFro = (uint32_t)kCLOCK_FroHf, /*!< Use FRO 96 MHz. */ + kCLOCK_UsbfsSrcPll0 = (uint32_t)kCLOCK_Pll0Out, /*!< Use PLL0 output. */ + kCLOCK_UsbfsSrcMainClock = (uint32_t)kCLOCK_CoreSysClk, /*!< Use Main clock. */ + kCLOCK_UsbfsSrcPll1 = (uint32_t)kCLOCK_Pll1Out, /*!< Use PLL1 clock. */ + + kCLOCK_UsbfsSrcNone = + SYSCON_USB0CLKSEL_SEL(7) /*! SIZE_MAX - alignbytes - sizeof(mem_align_cb_t)) + { + return NULL; + } + + alignedsize += alignbytes + sizeof(mem_align_cb_t); + + union + { + void *pointer_value; +#if (defined(__DSC__) && defined(__CW__)) + uint32_t unsigned_value; +#else + uintptr_t unsigned_value; +#endif + } p_align_addr, p_addr; + + p_addr.pointer_value = malloc(alignedsize); + + if (p_addr.pointer_value == NULL) + { + return NULL; + } + + p_align_addr.unsigned_value = SDK_SIZEALIGN(p_addr.unsigned_value + sizeof(mem_align_cb_t), alignbytes); + + p_cb = (mem_align_cb_t *)(p_align_addr.unsigned_value - 4U); + p_cb->identifier = SDK_MEM_MAGIC_NUMBER; + p_cb->offset = (uint16_t)(p_align_addr.unsigned_value - p_addr.unsigned_value); + + return p_align_addr.pointer_value; +} + +void SDK_Free(void *ptr) +{ + union + { + void *pointer_value; +#if (defined(__DSC__) && defined(__CW__)) + uint32_t unsigned_value; +#else + uintptr_t unsigned_value; +#endif + } p_free; + p_free.pointer_value = ptr; + mem_align_cb_t *p_cb = (mem_align_cb_t *)(p_free.unsigned_value - 4U); + + if (p_cb->identifier != SDK_MEM_MAGIC_NUMBER) + { + return; + } + + p_free.unsigned_value = p_free.unsigned_value - p_cb->offset; + + free(p_free.pointer_value); +} diff --git a/drivers/fsl_common.h b/drivers/fsl_common.h new file mode 100644 index 0000000..ddf49d6 --- /dev/null +++ b/drivers/fsl_common.h @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2015-2016, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _FSL_COMMON_H_ +#define _FSL_COMMON_H_ + +#include +#include +#include +#include +#include + +#if defined(__ICCARM__) || (defined(__CC_ARM) || defined(__ARMCC_VERSION)) || defined(__GNUC__) +#include +#endif + +#include "fsl_device_registers.h" + +/*! + * @addtogroup ksdk_common + * @{ + */ + +/******************************************************************************* + * Configurations + ******************************************************************************/ + +/*! @brief Macro to use the default weak IRQ handler in drivers. */ +#ifndef FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ +#define FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ 1 +#endif + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief Construct a status code value from a group and code number. */ +#define MAKE_STATUS(group, code) ((((group)*100L) + (code))) + +/*! @brief Construct the version number for drivers. + * + * The driver version is a 32-bit number, for both 32-bit platforms(such as Cortex M) + * and 16-bit platforms(such as DSC). + * + * @verbatim + + | Unused || Major Version || Minor Version || Bug Fix | + 31 25 24 17 16 9 8 0 + + @endverbatim + */ +#define MAKE_VERSION(major, minor, bugfix) (((major) * 65536L) + ((minor) * 256L) + (bugfix)) + +/*! @name Driver version */ +/*@{*/ +/*! @brief common driver version. */ +#define FSL_COMMON_DRIVER_VERSION (MAKE_VERSION(2, 3, 1)) +/*@}*/ + +/* Debug console type definition. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_NONE 0U /*!< No debug console. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_UART 1U /*!< Debug console based on UART. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_LPUART 2U /*!< Debug console based on LPUART. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_LPSCI 3U /*!< Debug console based on LPSCI. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_USBCDC 4U /*!< Debug console based on USBCDC. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_FLEXCOMM 5U /*!< Debug console based on FLEXCOMM. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_IUART 6U /*!< Debug console based on i.MX UART. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_VUSART 7U /*!< Debug console based on LPC_VUSART. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_MINI_USART 8U /*!< Debug console based on LPC_USART. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_SWO 9U /*!< Debug console based on SWO. */ +#define DEBUG_CONSOLE_DEVICE_TYPE_QSCI 10U /*!< Debug console based on QSCI. */ + +/*! @brief Status group numbers. */ +enum _status_groups +{ + kStatusGroup_Generic = 0, /*!< Group number for generic status codes. */ + kStatusGroup_FLASH = 1, /*!< Group number for FLASH status codes. */ + kStatusGroup_LPSPI = 4, /*!< Group number for LPSPI status codes. */ + kStatusGroup_FLEXIO_SPI = 5, /*!< Group number for FLEXIO SPI status codes. */ + kStatusGroup_DSPI = 6, /*!< Group number for DSPI status codes. */ + kStatusGroup_FLEXIO_UART = 7, /*!< Group number for FLEXIO UART status codes. */ + kStatusGroup_FLEXIO_I2C = 8, /*!< Group number for FLEXIO I2C status codes. */ + kStatusGroup_LPI2C = 9, /*!< Group number for LPI2C status codes. */ + kStatusGroup_UART = 10, /*!< Group number for UART status codes. */ + kStatusGroup_I2C = 11, /*!< Group number for UART status codes. */ + kStatusGroup_LPSCI = 12, /*!< Group number for LPSCI status codes. */ + kStatusGroup_LPUART = 13, /*!< Group number for LPUART status codes. */ + kStatusGroup_SPI = 14, /*!< Group number for SPI status code.*/ + kStatusGroup_XRDC = 15, /*!< Group number for XRDC status code.*/ + kStatusGroup_SEMA42 = 16, /*!< Group number for SEMA42 status code.*/ + kStatusGroup_SDHC = 17, /*!< Group number for SDHC status code */ + kStatusGroup_SDMMC = 18, /*!< Group number for SDMMC status code */ + kStatusGroup_SAI = 19, /*!< Group number for SAI status code */ + kStatusGroup_MCG = 20, /*!< Group number for MCG status codes. */ + kStatusGroup_SCG = 21, /*!< Group number for SCG status codes. */ + kStatusGroup_SDSPI = 22, /*!< Group number for SDSPI status codes. */ + kStatusGroup_FLEXIO_I2S = 23, /*!< Group number for FLEXIO I2S status codes */ + kStatusGroup_FLEXIO_MCULCD = 24, /*!< Group number for FLEXIO LCD status codes */ + kStatusGroup_FLASHIAP = 25, /*!< Group number for FLASHIAP status codes */ + kStatusGroup_FLEXCOMM_I2C = 26, /*!< Group number for FLEXCOMM I2C status codes */ + kStatusGroup_I2S = 27, /*!< Group number for I2S status codes */ + kStatusGroup_IUART = 28, /*!< Group number for IUART status codes */ + kStatusGroup_CSI = 29, /*!< Group number for CSI status codes */ + kStatusGroup_MIPI_DSI = 30, /*!< Group number for MIPI DSI status codes */ + kStatusGroup_SDRAMC = 35, /*!< Group number for SDRAMC status codes. */ + kStatusGroup_POWER = 39, /*!< Group number for POWER status codes. */ + kStatusGroup_ENET = 40, /*!< Group number for ENET status codes. */ + kStatusGroup_PHY = 41, /*!< Group number for PHY status codes. */ + kStatusGroup_TRGMUX = 42, /*!< Group number for TRGMUX status codes. */ + kStatusGroup_SMARTCARD = 43, /*!< Group number for SMARTCARD status codes. */ + kStatusGroup_LMEM = 44, /*!< Group number for LMEM status codes. */ + kStatusGroup_QSPI = 45, /*!< Group number for QSPI status codes. */ + kStatusGroup_DMA = 50, /*!< Group number for DMA status codes. */ + kStatusGroup_EDMA = 51, /*!< Group number for EDMA status codes. */ + kStatusGroup_DMAMGR = 52, /*!< Group number for DMAMGR status codes. */ + kStatusGroup_FLEXCAN = 53, /*!< Group number for FlexCAN status codes. */ + kStatusGroup_LTC = 54, /*!< Group number for LTC status codes. */ + kStatusGroup_FLEXIO_CAMERA = 55, /*!< Group number for FLEXIO CAMERA status codes. */ + kStatusGroup_LPC_SPI = 56, /*!< Group number for LPC_SPI status codes. */ + kStatusGroup_LPC_USART = 57, /*!< Group number for LPC_USART status codes. */ + kStatusGroup_DMIC = 58, /*!< Group number for DMIC status codes. */ + kStatusGroup_SDIF = 59, /*!< Group number for SDIF status codes.*/ + kStatusGroup_SPIFI = 60, /*!< Group number for SPIFI status codes. */ + kStatusGroup_OTP = 61, /*!< Group number for OTP status codes. */ + kStatusGroup_MCAN = 62, /*!< Group number for MCAN status codes. */ + kStatusGroup_CAAM = 63, /*!< Group number for CAAM status codes. */ + kStatusGroup_ECSPI = 64, /*!< Group number for ECSPI status codes. */ + kStatusGroup_USDHC = 65, /*!< Group number for USDHC status codes.*/ + kStatusGroup_LPC_I2C = 66, /*!< Group number for LPC_I2C status codes.*/ + kStatusGroup_DCP = 67, /*!< Group number for DCP status codes.*/ + kStatusGroup_MSCAN = 68, /*!< Group number for MSCAN status codes.*/ + kStatusGroup_ESAI = 69, /*!< Group number for ESAI status codes. */ + kStatusGroup_FLEXSPI = 70, /*!< Group number for FLEXSPI status codes. */ + kStatusGroup_MMDC = 71, /*!< Group number for MMDC status codes. */ + kStatusGroup_PDM = 72, /*!< Group number for MIC status codes. */ + kStatusGroup_SDMA = 73, /*!< Group number for SDMA status codes. */ + kStatusGroup_ICS = 74, /*!< Group number for ICS status codes. */ + kStatusGroup_SPDIF = 75, /*!< Group number for SPDIF status codes. */ + kStatusGroup_LPC_MINISPI = 76, /*!< Group number for LPC_MINISPI status codes. */ + kStatusGroup_HASHCRYPT = 77, /*!< Group number for Hashcrypt status codes */ + kStatusGroup_LPC_SPI_SSP = 78, /*!< Group number for LPC_SPI_SSP status codes. */ + kStatusGroup_I3C = 79, /*!< Group number for I3C status codes */ + kStatusGroup_LPC_I2C_1 = 97, /*!< Group number for LPC_I2C_1 status codes. */ + kStatusGroup_NOTIFIER = 98, /*!< Group number for NOTIFIER status codes. */ + kStatusGroup_DebugConsole = 99, /*!< Group number for debug console status codes. */ + kStatusGroup_SEMC = 100, /*!< Group number for SEMC status codes. */ + kStatusGroup_ApplicationRangeStart = 101, /*!< Starting number for application groups. */ + kStatusGroup_IAP = 102, /*!< Group number for IAP status codes */ + kStatusGroup_SFA = 103, /*!< Group number for SFA status codes*/ + kStatusGroup_SPC = 104, /*!< Group number for SPC status codes. */ + kStatusGroup_PUF = 105, /*!< Group number for PUF status codes. */ + kStatusGroup_TOUCH_PANEL = 106, /*!< Group number for touch panel status codes */ + + kStatusGroup_HAL_GPIO = 121, /*!< Group number for HAL GPIO status codes. */ + kStatusGroup_HAL_UART = 122, /*!< Group number for HAL UART status codes. */ + kStatusGroup_HAL_TIMER = 123, /*!< Group number for HAL TIMER status codes. */ + kStatusGroup_HAL_SPI = 124, /*!< Group number for HAL SPI status codes. */ + kStatusGroup_HAL_I2C = 125, /*!< Group number for HAL I2C status codes. */ + kStatusGroup_HAL_FLASH = 126, /*!< Group number for HAL FLASH status codes. */ + kStatusGroup_HAL_PWM = 127, /*!< Group number for HAL PWM status codes. */ + kStatusGroup_HAL_RNG = 128, /*!< Group number for HAL RNG status codes. */ + kStatusGroup_HAL_I2S = 129, /*!< Group number for HAL I2S status codes. */ + kStatusGroup_TIMERMANAGER = 135, /*!< Group number for TiMER MANAGER status codes. */ + kStatusGroup_SERIALMANAGER = 136, /*!< Group number for SERIAL MANAGER status codes. */ + kStatusGroup_LED = 137, /*!< Group number for LED status codes. */ + kStatusGroup_BUTTON = 138, /*!< Group number for BUTTON status codes. */ + kStatusGroup_EXTERN_EEPROM = 139, /*!< Group number for EXTERN EEPROM status codes. */ + kStatusGroup_SHELL = 140, /*!< Group number for SHELL status codes. */ + kStatusGroup_MEM_MANAGER = 141, /*!< Group number for MEM MANAGER status codes. */ + kStatusGroup_LIST = 142, /*!< Group number for List status codes. */ + kStatusGroup_OSA = 143, /*!< Group number for OSA status codes. */ + kStatusGroup_COMMON_TASK = 144, /*!< Group number for Common task status codes. */ + kStatusGroup_MSG = 145, /*!< Group number for messaging status codes. */ + kStatusGroup_SDK_OCOTP = 146, /*!< Group number for OCOTP status codes. */ + kStatusGroup_SDK_FLEXSPINOR = 147, /*!< Group number for FLEXSPINOR status codes.*/ + kStatusGroup_CODEC = 148, /*!< Group number for codec status codes. */ + kStatusGroup_ASRC = 149, /*!< Group number for codec status ASRC. */ + kStatusGroup_OTFAD = 150, /*!< Group number for codec status codes. */ + kStatusGroup_SDIOSLV = 151, /*!< Group number for SDIOSLV status codes. */ + kStatusGroup_MECC = 152, /*!< Group number for MECC status codes. */ + kStatusGroup_ENET_QOS = 153, /*!< Group number for ENET_QOS status codes. */ + kStatusGroup_LOG = 154, /*!< Group number for LOG status codes. */ + kStatusGroup_I3CBUS = 155, /*!< Group number for I3CBUS status codes. */ + kStatusGroup_QSCI = 156, /*!< Group number for QSCI status codes. */ + kStatusGroup_SNT = 157, /*!< Group number for SNT status codes. */ + kStatusGroup_QUEUEDSPI = 158, /*!< Group number for QSPI status codes. */ + kStatusGroup_POWER_MANAGER = 159, /*!< Group number for POWER_MANAGER status codes. */ +}; + +/*! \public + * @brief Generic status return codes. + */ +enum +{ + kStatus_Success = MAKE_STATUS(kStatusGroup_Generic, 0), /*!< Generic status for Success. */ + kStatus_Fail = MAKE_STATUS(kStatusGroup_Generic, 1), /*!< Generic status for Fail. */ + kStatus_ReadOnly = MAKE_STATUS(kStatusGroup_Generic, 2), /*!< Generic status for read only failure. */ + kStatus_OutOfRange = MAKE_STATUS(kStatusGroup_Generic, 3), /*!< Generic status for out of range access. */ + kStatus_InvalidArgument = MAKE_STATUS(kStatusGroup_Generic, 4), /*!< Generic status for invalid argument check. */ + kStatus_Timeout = MAKE_STATUS(kStatusGroup_Generic, 5), /*!< Generic status for timeout. */ + kStatus_NoTransferInProgress = + MAKE_STATUS(kStatusGroup_Generic, 6), /*!< Generic status for no transfer in progress. */ + kStatus_Busy = MAKE_STATUS(kStatusGroup_Generic, 7), /*!< Generic status for module is busy. */ + kStatus_NoData = + MAKE_STATUS(kStatusGroup_Generic, 8), /*!< Generic status for no data is found for the operation. */ +}; + +/*! @brief Type used for all status and error return values. */ +typedef int32_t status_t; + +/*! + * @name Min/max macros + * @{ + */ +#if !defined(MIN) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#if !defined(MAX) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif +/* @} */ + +/*! @brief Computes the number of elements in an array. */ +#if !defined(ARRAY_SIZE) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/*! @name UINT16_MAX/UINT32_MAX value */ +/* @{ */ +#if !defined(UINT16_MAX) +#define UINT16_MAX ((uint16_t)-1) +#endif + +#if !defined(UINT32_MAX) +#define UINT32_MAX ((uint32_t)-1) +#endif +/* @} */ + +/*! @name Suppress fallthrough warning macro */ +/* For switch case code block, if case section ends without "break;" statement, there wil be + fallthrough warning with compiler flag -Wextra or -Wimplicit-fallthrough=n when using armgcc. + To suppress this warning, "SUPPRESS_FALL_THROUGH_WARNING();" need to be added at the end of each + case section which misses "break;"statement. + */ +/* @{ */ +#if defined(__GNUC__) && !defined(__ARMCC_VERSION) +#define SUPPRESS_FALL_THROUGH_WARNING() __attribute__((fallthrough)) +#else +#define SUPPRESS_FALL_THROUGH_WARNING() +#endif +/* @} */ + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @brief Allocate memory with given alignment and aligned size. + * + * This is provided to support the dynamically allocated memory + * used in cache-able region. + * @param size The length required to malloc. + * @param alignbytes The alignment size. + * @retval The allocated memory. + */ +void *SDK_Malloc(size_t size, size_t alignbytes); + +/*! + * @brief Free memory. + * + * @param ptr The memory to be release. + */ +void SDK_Free(void *ptr); + +/*! + * @brief Delay at least for some time. + * Please note that, this API uses while loop for delay, different run-time environments make the time not precise, + * if precise delay count was needed, please implement a new delay function with hardware timer. + * + * @param delayTime_us Delay time in unit of microsecond. + * @param coreClock_Hz Core clock frequency with Hz. + */ +void SDK_DelayAtLeastUs(uint32_t delayTime_us, uint32_t coreClock_Hz); + +#if defined(__cplusplus) +} +#endif + +/*! @} */ + +#if (defined(__DSC__) && defined(__CW__)) +#include "fsl_common_dsc.h" +#elif defined(__XCC__) +#include "fsl_common_dsp.h" +#else +#include "fsl_common_arm.h" +#endif + +#endif /* _FSL_COMMON_H_ */ diff --git a/drivers/fsl_common_arm.c b/drivers/fsl_common_arm.c new file mode 100644 index 0000000..67fe381 --- /dev/null +++ b/drivers/fsl_common_arm.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2015-2016, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_common.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.common_arm" +#endif + +#ifndef __GIC_PRIO_BITS +#if defined(ENABLE_RAM_VECTOR_TABLE) +uint32_t InstallIRQHandler(IRQn_Type irq, uint32_t irqHandler) +{ +#ifdef __VECTOR_TABLE +#undef __VECTOR_TABLE +#endif + +/* Addresses for VECTOR_TABLE and VECTOR_RAM come from the linker file */ +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) + extern uint32_t Image$$VECTOR_ROM$$Base[]; + extern uint32_t Image$$VECTOR_RAM$$Base[]; + extern uint32_t Image$$RW_m_data$$Base[]; + +#define __VECTOR_TABLE Image$$VECTOR_ROM$$Base +#define __VECTOR_RAM Image$$VECTOR_RAM$$Base +#define __RAM_VECTOR_TABLE_SIZE (((uint32_t)Image$$RW_m_data$$Base - (uint32_t)Image$$VECTOR_RAM$$Base)) +#elif defined(__ICCARM__) + extern uint32_t __RAM_VECTOR_TABLE_SIZE[]; + extern uint32_t __VECTOR_TABLE[]; + extern uint32_t __VECTOR_RAM[]; +#elif defined(__GNUC__) + extern uint32_t __VECTOR_TABLE[]; + extern uint32_t __VECTOR_RAM[]; + extern uint32_t __RAM_VECTOR_TABLE_SIZE_BYTES[]; + uint32_t __RAM_VECTOR_TABLE_SIZE = (uint32_t)(__RAM_VECTOR_TABLE_SIZE_BYTES); +#endif /* defined(__CC_ARM) || defined(__ARMCC_VERSION) */ + uint32_t n; + uint32_t ret; + uint32_t irqMaskValue; + + irqMaskValue = DisableGlobalIRQ(); + if (SCB->VTOR != (uint32_t)__VECTOR_RAM) + { + /* Copy the vector table from ROM to RAM */ + for (n = 0; n < ((uint32_t)__RAM_VECTOR_TABLE_SIZE) / sizeof(uint32_t); n++) + { + __VECTOR_RAM[n] = __VECTOR_TABLE[n]; + } + /* Point the VTOR to the position of vector table */ + SCB->VTOR = (uint32_t)__VECTOR_RAM; + } + + ret = __VECTOR_RAM[(int32_t)irq + 16]; + /* make sure the __VECTOR_RAM is noncachable */ + __VECTOR_RAM[(int32_t)irq + 16] = irqHandler; + + EnableGlobalIRQ(irqMaskValue); + + return ret; +} +#endif /* ENABLE_RAM_VECTOR_TABLE. */ +#endif /* __GIC_PRIO_BITS. */ + +#if (defined(FSL_FEATURE_SOC_SYSCON_COUNT) && (FSL_FEATURE_SOC_SYSCON_COUNT > 0)) + +/* + * When FSL_FEATURE_POWERLIB_EXTEND is defined to non-zero value, + * powerlib should be used instead of these functions. + */ +#if !(defined(FSL_FEATURE_POWERLIB_EXTEND) && (FSL_FEATURE_POWERLIB_EXTEND != 0)) + +/* + * When the SYSCON STARTER registers are discontinuous, these functions are + * implemented in fsl_power.c. + */ +#if !(defined(FSL_FEATURE_SYSCON_STARTER_DISCONTINUOUS) && FSL_FEATURE_SYSCON_STARTER_DISCONTINUOUS) + +void EnableDeepSleepIRQ(IRQn_Type interrupt) +{ + uint32_t intNumber = (uint32_t)interrupt; + + uint32_t index = 0; + + while (intNumber >= 32u) + { + index++; + intNumber -= 32u; + } + + SYSCON->STARTERSET[index] = 1UL << intNumber; + (void)EnableIRQ(interrupt); /* also enable interrupt at NVIC */ +} + +void DisableDeepSleepIRQ(IRQn_Type interrupt) +{ + uint32_t intNumber = (uint32_t)interrupt; + + (void)DisableIRQ(interrupt); /* also disable interrupt at NVIC */ + uint32_t index = 0; + + while (intNumber >= 32u) + { + index++; + intNumber -= 32u; + } + + SYSCON->STARTERCLR[index] = 1UL << intNumber; +} +#endif /* FSL_FEATURE_SYSCON_STARTER_DISCONTINUOUS */ +#endif /* FSL_FEATURE_POWERLIB_EXTEND */ +#endif /* FSL_FEATURE_SOC_SYSCON_COUNT */ + +#if defined(SDK_DELAY_USE_DWT) && defined(DWT) +/* Use WDT. */ +static void enableCpuCycleCounter(void) +{ + /* Make sure the DWT trace fucntion is enabled. */ + if (CoreDebug_DEMCR_TRCENA_Msk != (CoreDebug_DEMCR_TRCENA_Msk & CoreDebug->DEMCR)) + { + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + } + + /* CYCCNT not supported on this device. */ + assert(DWT_CTRL_NOCYCCNT_Msk != (DWT->CTRL & DWT_CTRL_NOCYCCNT_Msk)); + + /* Read CYCCNT directly if CYCCENT has already been enabled, otherwise enable CYCCENT first. */ + if (DWT_CTRL_CYCCNTENA_Msk != (DWT_CTRL_CYCCNTENA_Msk & DWT->CTRL)) + { + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + } +} + +static uint32_t getCpuCycleCount(void) +{ + return DWT->CYCCNT; +} +#else /* defined(SDK_DELAY_USE_DWT) && defined(DWT) */ +/* Use software loop. */ +#if defined(__CC_ARM) /* This macro is arm v5 specific */ +/* clang-format off */ +__ASM static void DelayLoop(uint32_t count) +{ +loop + SUBS R0, R0, #1 + CMP R0, #0 + BNE loop + BX LR +} +/* clang-format on */ +#elif defined(__ARMCC_VERSION) || defined(__ICCARM__) || defined(__GNUC__) +/* Cortex-M0 has a smaller instruction set, SUBS isn't supported in thumb-16 mode reported from __GNUC__ compiler, + * use SUB and CMP here for compatibility */ +static void DelayLoop(uint32_t count) +{ + __ASM volatile(" MOV R0, %0" : : "r"(count)); + __ASM volatile( + "loop: \n" +#if defined(__GNUC__) && !defined(__ARMCC_VERSION) + " SUB R0, R0, #1 \n" +#else + " SUBS R0, R0, #1 \n" +#endif + " CMP R0, #0 \n" + + " BNE loop \n" + : + : + : "r0"); +} +#endif /* defined(__CC_ARM) */ +#endif /* defined(SDK_DELAY_USE_DWT) && defined(DWT) */ + +/*! + * @brief Delay at least for some time. + * Please note that, if not uses DWT, this API will use while loop for delay, different run-time environments have + * effect on the delay time. If precise delay is needed, please enable DWT delay. The two parmeters delayTime_us and + * coreClock_Hz have limitation. For example, in the platform with 1GHz coreClock_Hz, the delayTime_us only supports + * up to 4294967 in current code. If long time delay is needed, please implement a new delay function. + * + * @param delayTime_us Delay time in unit of microsecond. + * @param coreClock_Hz Core clock frequency with Hz. + */ +void SDK_DelayAtLeastUs(uint32_t delayTime_us, uint32_t coreClock_Hz) +{ + uint64_t count; + + if (delayTime_us > 0U) + { + count = USEC_TO_COUNT(delayTime_us, coreClock_Hz); + + assert(count <= UINT32_MAX); + +#if defined(SDK_DELAY_USE_DWT) && defined(DWT) /* Use DWT for better accuracy */ + + enableCpuCycleCounter(); + /* Calculate the count ticks. */ + count += getCpuCycleCount(); + + if (count > UINT32_MAX) + { + count -= UINT32_MAX; + /* Wait for cyccnt overflow. */ + while (count < getCpuCycleCount()) + { + } + } + + /* Wait for cyccnt reach count value. */ + while (count > getCpuCycleCount()) + { + } +#else + /* Divide value may be different in various environment to ensure delay is precise. + * Every loop count includes three instructions, due to Cortex-M7 sometimes executes + * two instructions in one period, through test here set divide 1.5. Other M cores use + * divide 4. By the way, divide 1.5 or 4 could let the count lose precision, but it does + * not matter because other instructions outside while loop is enough to fill the time. + */ +#if (__CORTEX_M == 7) + count = count / 3U * 2U; +#else + count = count / 4U; +#endif + DelayLoop((uint32_t)count); +#endif /* defined(SDK_DELAY_USE_DWT) && defined(DWT) */ + } +} diff --git a/drivers/fsl_common_arm.h b/drivers/fsl_common_arm.h new file mode 100644 index 0000000..ed7d5f4 --- /dev/null +++ b/drivers/fsl_common_arm.h @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2015-2016, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _FSL_COMMON_ARM_H_ +#define _FSL_COMMON_ARM_H_ + +/* + * For CMSIS pack RTE. + * CMSIS pack RTE generates "RTC_Components.h" which contains the statements + * of the related element for all selected software components. + */ +#ifdef _RTE_ +#include "RTE_Components.h" +#endif + +/*! + * @addtogroup ksdk_common + * @{ + */ + +/*! @name Atomic modification + * + * These macros are used for atomic access, such as read-modify-write + * to the peripheral registers. + * + * - SDK_ATOMIC_LOCAL_ADD + * - SDK_ATOMIC_LOCAL_SET + * - SDK_ATOMIC_LOCAL_CLEAR + * - SDK_ATOMIC_LOCAL_TOGGLE + * - SDK_ATOMIC_LOCAL_CLEAR_AND_SET + * + * Take SDK_ATOMIC_LOCAL_CLEAR_AND_SET as an example: the parameter @c addr + * means the address of the peripheral register or variable you want to modify + * atomically, the parameter @c clearBits is the bits to clear, the parameter + * @c setBits it the bits to set. + * For example, to set a 32-bit register bit1:bit0 to 0b10, use like this: + * + * @code + volatile uint32_t * reg = (volatile uint32_t *)REG_ADDR; + + SDK_ATOMIC_LOCAL_CLEAR_AND_SET(reg, 0x03, 0x02); + @endcode + * + * In this example, the register bit1:bit0 are cleared and bit1 is set, as a result, + * register bit1:bit0 = 0b10. + * + * @note For the platforms don't support exclusive load and store, these macros + * disable the global interrupt to pretect the modification. + * + * @note These macros only guarantee the local processor atomic operations. For + * the multi-processor devices, use hardware semaphore such as SEMA42 to + * guarantee exclusive access if necessary. + * + * @{ + */ + +/* clang-format off */ +#if ((defined(__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined(__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined(__ARM_ARCH_8M_MAIN__) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined(__ARM_ARCH_8M_BASE__) && (__ARM_ARCH_8M_BASE__ == 1))) +/* clang-format on */ + +/* If the LDREX and STREX are supported, use them. */ +#define _SDK_ATOMIC_LOCAL_OPS_1BYTE(addr, val, ops) \ + do \ + { \ + (val) = __LDREXB(addr); \ + (ops); \ + } while (0UL != __STREXB((val), (addr))) + +#define _SDK_ATOMIC_LOCAL_OPS_2BYTE(addr, val, ops) \ + do \ + { \ + (val) = __LDREXH(addr); \ + (ops); \ + } while (0UL != __STREXH((val), (addr))) + +#define _SDK_ATOMIC_LOCAL_OPS_4BYTE(addr, val, ops) \ + do \ + { \ + (val) = __LDREXW(addr); \ + (ops); \ + } while (0UL != __STREXW((val), (addr))) + +static inline void _SDK_AtomicLocalAdd1Byte(volatile uint8_t *addr, uint8_t val) +{ + uint8_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_1BYTE(addr, s_val, s_val += val); +} + +static inline void _SDK_AtomicLocalAdd2Byte(volatile uint16_t *addr, uint16_t val) +{ + uint16_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_2BYTE(addr, s_val, s_val += val); +} + +static inline void _SDK_AtomicLocalAdd4Byte(volatile uint32_t *addr, uint32_t val) +{ + uint32_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_4BYTE(addr, s_val, s_val += val); +} + +static inline void _SDK_AtomicLocalSub1Byte(volatile uint8_t *addr, uint8_t val) +{ + uint8_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_1BYTE(addr, s_val, s_val -= val); +} + +static inline void _SDK_AtomicLocalSub2Byte(volatile uint16_t *addr, uint16_t val) +{ + uint16_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_2BYTE(addr, s_val, s_val -= val); +} + +static inline void _SDK_AtomicLocalSub4Byte(volatile uint32_t *addr, uint32_t val) +{ + uint32_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_4BYTE(addr, s_val, s_val -= val); +} + +static inline void _SDK_AtomicLocalSet1Byte(volatile uint8_t *addr, uint8_t bits) +{ + uint8_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_1BYTE(addr, s_val, s_val |= bits); +} + +static inline void _SDK_AtomicLocalSet2Byte(volatile uint16_t *addr, uint16_t bits) +{ + uint16_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_2BYTE(addr, s_val, s_val |= bits); +} + +static inline void _SDK_AtomicLocalSet4Byte(volatile uint32_t *addr, uint32_t bits) +{ + uint32_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_4BYTE(addr, s_val, s_val |= bits); +} + +static inline void _SDK_AtomicLocalClear1Byte(volatile uint8_t *addr, uint8_t bits) +{ + uint8_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_1BYTE(addr, s_val, s_val &= ~bits); +} + +static inline void _SDK_AtomicLocalClear2Byte(volatile uint16_t *addr, uint16_t bits) +{ + uint16_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_2BYTE(addr, s_val, s_val &= ~bits); +} + +static inline void _SDK_AtomicLocalClear4Byte(volatile uint32_t *addr, uint32_t bits) +{ + uint32_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_4BYTE(addr, s_val, s_val &= ~bits); +} + +static inline void _SDK_AtomicLocalToggle1Byte(volatile uint8_t *addr, uint8_t bits) +{ + uint8_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_1BYTE(addr, s_val, s_val ^= bits); +} + +static inline void _SDK_AtomicLocalToggle2Byte(volatile uint16_t *addr, uint16_t bits) +{ + uint16_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_2BYTE(addr, s_val, s_val ^= bits); +} + +static inline void _SDK_AtomicLocalToggle4Byte(volatile uint32_t *addr, uint32_t bits) +{ + uint32_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_4BYTE(addr, s_val, s_val ^= bits); +} + +static inline void _SDK_AtomicLocalClearAndSet1Byte(volatile uint8_t *addr, uint8_t clearBits, uint8_t setBits) +{ + uint8_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_1BYTE(addr, s_val, s_val = (s_val & ~clearBits) | setBits); +} + +static inline void _SDK_AtomicLocalClearAndSet2Byte(volatile uint16_t *addr, uint16_t clearBits, uint16_t setBits) +{ + uint16_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_2BYTE(addr, s_val, s_val = (s_val & ~clearBits) | setBits); +} + +static inline void _SDK_AtomicLocalClearAndSet4Byte(volatile uint32_t *addr, uint32_t clearBits, uint32_t setBits) +{ + uint32_t s_val; + + _SDK_ATOMIC_LOCAL_OPS_4BYTE(addr, s_val, s_val = (s_val & ~clearBits) | setBits); +} + +#define SDK_ATOMIC_LOCAL_ADD(addr, val) \ + ((1UL == sizeof(*(addr))) ? \ + _SDK_AtomicLocalAdd1Byte((volatile uint8_t *)(volatile void *)(addr), (uint8_t)(val)) : \ + ((2UL == sizeof(*(addr))) ? _SDK_AtomicLocalAdd2Byte((volatile uint16_t *)(volatile void *)(addr), (uint16_t)(val)) : \ + _SDK_AtomicLocalAdd4Byte((volatile uint32_t *)(volatile void *)(addr), (uint32_t)(val)))) + +#define SDK_ATOMIC_LOCAL_SET(addr, bits) \ + ((1UL == sizeof(*(addr))) ? \ + _SDK_AtomicLocalSet1Byte((volatile uint8_t *)(volatile void *)(addr), (uint8_t)(bits)) : \ + ((2UL == sizeof(*(addr))) ? _SDK_AtomicLocalSet2Byte((volatile uint16_t *)(volatile void *)(addr), (uint16_t)(bits)) : \ + _SDK_AtomicLocalSet4Byte((volatile uint32_t *)(volatile void *)(addr), (uint32_t)(bits)))) + +#define SDK_ATOMIC_LOCAL_CLEAR(addr, bits) \ + ((1UL == sizeof(*(addr))) ? \ + _SDK_AtomicLocalClear1Byte((volatile uint8_t *)(volatile void *)(addr), (uint8_t)(bits)) : \ + ((2UL == sizeof(*(addr))) ? \ + _SDK_AtomicLocalClear2Byte((volatile uint16_t *)(volatile void *)(addr), (uint16_t)(bits)) : \ + _SDK_AtomicLocalClear4Byte((volatile uint32_t *)(volatile void *)(addr), (uint32_t)(bits)))) + +#define SDK_ATOMIC_LOCAL_TOGGLE(addr, bits) \ + ((1UL == sizeof(*(addr))) ? \ + _SDK_AtomicLocalToggle1Byte((volatile uint8_t *)(volatile void *)(addr), (uint8_t)(bits)) : \ + ((2UL == sizeof(*(addr))) ? \ + _SDK_AtomicLocalToggle2Byte((volatile uint16_t *)(volatile void *)(addr), (uint16_t)(bits)) : \ + _SDK_AtomicLocalToggle4Byte((volatile uint32_t *)(volatile void *)(addr), (uint32_t)(bits)))) + +#define SDK_ATOMIC_LOCAL_CLEAR_AND_SET(addr, clearBits, setBits) \ + ((1UL == sizeof(*(addr))) ? \ + _SDK_AtomicLocalClearAndSet1Byte((volatile uint8_t *)(volatile void *)(addr), (uint8_t)(clearBits), (uint8_t)(setBits)) : \ + ((2UL == sizeof(*(addr))) ? \ + _SDK_AtomicLocalClearAndSet2Byte((volatile uint16_t *)(volatile void *)(addr), (uint16_t)(clearBits), (uint16_t)(setBits)) : \ + _SDK_AtomicLocalClearAndSet4Byte((volatile uint32_t *)(volatile void *)(addr), (uint32_t)(clearBits), (uint32_t)(setBits)))) +#else + +#define SDK_ATOMIC_LOCAL_ADD(addr, val) \ + do \ + { \ + uint32_t s_atomicOldInt; \ + s_atomicOldInt = DisableGlobalIRQ(); \ + *(addr) += (val); \ + EnableGlobalIRQ(s_atomicOldInt); \ + } while (0) + +#define SDK_ATOMIC_LOCAL_SET(addr, bits) \ + do \ + { \ + uint32_t s_atomicOldInt; \ + s_atomicOldInt = DisableGlobalIRQ(); \ + *(addr) |= (bits); \ + EnableGlobalIRQ(s_atomicOldInt); \ + } while (0) + +#define SDK_ATOMIC_LOCAL_CLEAR(addr, bits) \ + do \ + { \ + uint32_t s_atomicOldInt; \ + s_atomicOldInt = DisableGlobalIRQ(); \ + *(addr) &= ~(bits); \ + EnableGlobalIRQ(s_atomicOldInt); \ + } while (0) + +#define SDK_ATOMIC_LOCAL_TOGGLE(addr, bits) \ + do \ + { \ + uint32_t s_atomicOldInt; \ + s_atomicOldInt = DisableGlobalIRQ(); \ + *(addr) ^= (bits); \ + EnableGlobalIRQ(s_atomicOldInt); \ + } while (0) + +#define SDK_ATOMIC_LOCAL_CLEAR_AND_SET(addr, clearBits, setBits) \ + do \ + { \ + uint32_t s_atomicOldInt; \ + s_atomicOldInt = DisableGlobalIRQ(); \ + *(addr) = (*(addr) & ~(clearBits)) | (setBits); \ + EnableGlobalIRQ(s_atomicOldInt); \ + } while (0) + +#endif +/* @} */ + +/*! @name Timer utilities */ +/* @{ */ +/*! Macro to convert a microsecond period to raw count value */ +#define USEC_TO_COUNT(us, clockFreqInHz) (uint64_t)(((uint64_t)(us) * (clockFreqInHz)) / 1000000U) +/*! Macro to convert a raw count value to microsecond */ +#define COUNT_TO_USEC(count, clockFreqInHz) (uint64_t)((uint64_t)(count)*1000000U / (clockFreqInHz)) + +/*! Macro to convert a millisecond period to raw count value */ +#define MSEC_TO_COUNT(ms, clockFreqInHz) (uint64_t)((uint64_t)(ms) * (clockFreqInHz) / 1000U) +/*! Macro to convert a raw count value to millisecond */ +#define COUNT_TO_MSEC(count, clockFreqInHz) (uint64_t)((uint64_t)(count)*1000U / (clockFreqInHz)) +/* @} */ + +/*! @name ISR exit barrier + * @{ + * + * ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + * exception return operation might vector to incorrect interrupt. + * For Cortex-M7, if core speed much faster than peripheral register write speed, + * the peripheral interrupt flags may be still set after exiting ISR, this results to + * the same error similar with errata 83869. + */ +#if (defined __CORTEX_M) && ((__CORTEX_M == 4U) || (__CORTEX_M == 7U)) +#define SDK_ISR_EXIT_BARRIER __DSB() +#else +#define SDK_ISR_EXIT_BARRIER +#endif + +/* @} */ + +/*! @name Alignment variable definition macros */ +/* @{ */ +#if (defined(__ICCARM__)) +/* + * Workaround to disable MISRA C message suppress warnings for IAR compiler. + * http:/ /supp.iar.com/Support/?note=24725 + */ +_Pragma("diag_suppress=Pm120") +#define SDK_PRAGMA(x) _Pragma(#x) + _Pragma("diag_error=Pm120") +/*! Macro to define a variable with alignbytes alignment */ +#define SDK_ALIGN(var, alignbytes) SDK_PRAGMA(data_alignment = alignbytes) var +#elif defined(__CC_ARM) || defined(__ARMCC_VERSION) +/*! Macro to define a variable with alignbytes alignment */ +#define SDK_ALIGN(var, alignbytes) __attribute__((aligned(alignbytes))) var +#elif defined(__GNUC__) +/*! Macro to define a variable with alignbytes alignment */ +#define SDK_ALIGN(var, alignbytes) var __attribute__((aligned(alignbytes))) +#else +#error Toolchain not supported +#endif + +/*! Macro to define a variable with L1 d-cache line size alignment */ +#if defined(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE) +#define SDK_L1DCACHE_ALIGN(var) SDK_ALIGN(var, FSL_FEATURE_L1DCACHE_LINESIZE_BYTE) +#endif +/*! Macro to define a variable with L2 cache line size alignment */ +#if defined(FSL_FEATURE_L2CACHE_LINESIZE_BYTE) +#define SDK_L2CACHE_ALIGN(var) SDK_ALIGN(var, FSL_FEATURE_L2CACHE_LINESIZE_BYTE) +#endif + +/*! Macro to change a value to a given size aligned value */ +#define SDK_SIZEALIGN(var, alignbytes) \ + ((unsigned int)((var) + ((alignbytes)-1U)) & (unsigned int)(~(unsigned int)((alignbytes)-1U))) +/* @} */ + +/*! @name Non-cacheable region definition macros */ +/* For initialized non-zero non-cacheable variables, please using "AT_NONCACHEABLE_SECTION_INIT(var) ={xx};" or + * "AT_NONCACHEABLE_SECTION_ALIGN_INIT(var) ={xx};" in your projects to define them, for zero-inited non-cacheable + * variables, please using "AT_NONCACHEABLE_SECTION(var);" or "AT_NONCACHEABLE_SECTION_ALIGN(var);" to define them, + * these zero-inited variables will be initialized to zero in system startup. + */ +/* @{ */ + +#if ((!(defined(FSL_FEATURE_HAS_NO_NONCACHEABLE_SECTION) && FSL_FEATURE_HAS_NO_NONCACHEABLE_SECTION)) && \ + defined(FSL_FEATURE_L1ICACHE_LINESIZE_BYTE)) + +#if (defined(__ICCARM__)) +#define AT_NONCACHEABLE_SECTION(var) var @"NonCacheable" +#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) SDK_PRAGMA(data_alignment = alignbytes) var @"NonCacheable" +#define AT_NONCACHEABLE_SECTION_INIT(var) var @"NonCacheable.init" +#define AT_NONCACHEABLE_SECTION_ALIGN_INIT(var, alignbytes) \ + SDK_PRAGMA(data_alignment = alignbytes) var @"NonCacheable.init" + +#elif (defined(__CC_ARM) || defined(__ARMCC_VERSION)) +#define AT_NONCACHEABLE_SECTION_INIT(var) __attribute__((section("NonCacheable.init"))) var +#define AT_NONCACHEABLE_SECTION_ALIGN_INIT(var, alignbytes) \ + __attribute__((section("NonCacheable.init"))) __attribute__((aligned(alignbytes))) var +#if (defined(__CC_ARM)) +#define AT_NONCACHEABLE_SECTION(var) __attribute__((section("NonCacheable"), zero_init)) var +#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) \ + __attribute__((section("NonCacheable"), zero_init)) __attribute__((aligned(alignbytes))) var +#else +#define AT_NONCACHEABLE_SECTION(var) __attribute__((section(".bss.NonCacheable"))) var +#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) \ + __attribute__((section(".bss.NonCacheable"))) __attribute__((aligned(alignbytes))) var +#endif + +#elif (defined(__GNUC__)) +/* For GCC, when the non-cacheable section is required, please define "__STARTUP_INITIALIZE_NONCACHEDATA" + * in your projects to make sure the non-cacheable section variables will be initialized in system startup. + */ +#define AT_NONCACHEABLE_SECTION_INIT(var) __attribute__((section("NonCacheable.init"))) var +#define AT_NONCACHEABLE_SECTION_ALIGN_INIT(var, alignbytes) \ + __attribute__((section("NonCacheable.init"))) var __attribute__((aligned(alignbytes))) +#define AT_NONCACHEABLE_SECTION(var) __attribute__((section("NonCacheable,\"aw\",%nobits @"))) var +#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) \ + __attribute__((section("NonCacheable,\"aw\",%nobits @"))) var __attribute__((aligned(alignbytes))) +#else +#error Toolchain not supported. +#endif + +#else + +#define AT_NONCACHEABLE_SECTION(var) var +#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) SDK_ALIGN(var, alignbytes) +#define AT_NONCACHEABLE_SECTION_INIT(var) var +#define AT_NONCACHEABLE_SECTION_ALIGN_INIT(var, alignbytes) SDK_ALIGN(var, alignbytes) + +#endif + +/* @} */ + +/*! + * @name Time sensitive region + * @{ + */ +#if (defined(__ICCARM__)) +#define AT_QUICKACCESS_SECTION_CODE(func) func @"CodeQuickAccess" +#define AT_QUICKACCESS_SECTION_DATA(var) var @"DataQuickAccess" +#define AT_QUICKACCESS_SECTION_DATA_ALIGN(var, alignbytes) \ + SDK_PRAGMA(data_alignment = alignbytes) var @"DataQuickAccess" +#elif (defined(__CC_ARM) || defined(__ARMCC_VERSION)) +#define AT_QUICKACCESS_SECTION_CODE(func) __attribute__((section("CodeQuickAccess"), __noinline__)) func +#define AT_QUICKACCESS_SECTION_DATA(var) __attribute__((section("DataQuickAccess"))) var +#define AT_QUICKACCESS_SECTION_DATA_ALIGN(var, alignbytes) \ + __attribute__((section("DataQuickAccess"))) __attribute__((aligned(alignbytes))) var +#elif (defined(__GNUC__)) +#define AT_QUICKACCESS_SECTION_CODE(func) __attribute__((section("CodeQuickAccess"), __noinline__)) func +#define AT_QUICKACCESS_SECTION_DATA(var) __attribute__((section("DataQuickAccess"))) var +#define AT_QUICKACCESS_SECTION_DATA_ALIGN(var, alignbytes) \ + __attribute__((section("DataQuickAccess"))) var __attribute__((aligned(alignbytes))) +#else +#error Toolchain not supported. +#endif /* defined(__ICCARM__) */ + +/*! @name Ram Function */ +#if (defined(__ICCARM__)) +#define RAMFUNCTION_SECTION_CODE(func) func @"RamFunction" +#elif (defined(__CC_ARM) || defined(__ARMCC_VERSION)) +#define RAMFUNCTION_SECTION_CODE(func) __attribute__((section("RamFunction"))) func +#elif (defined(__GNUC__)) +#define RAMFUNCTION_SECTION_CODE(func) __attribute__((section("RamFunction"))) func +#else +#error Toolchain not supported. +#endif /* defined(__ICCARM__) */ +/* @} */ + +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + void DefaultISR(void); +#endif + +/* + * The fsl_clock.h is included here because it needs MAKE_VERSION/MAKE_STATUS/status_t + * defined in previous of this file. + */ +#include "fsl_clock.h" + +/* + * Chip level peripheral reset API, for MCUs that implement peripheral reset control external to a peripheral + */ +#if ((defined(FSL_FEATURE_SOC_SYSCON_COUNT) && (FSL_FEATURE_SOC_SYSCON_COUNT > 0)) || \ + (defined(FSL_FEATURE_SOC_ASYNC_SYSCON_COUNT) && (FSL_FEATURE_SOC_ASYNC_SYSCON_COUNT > 0))) +#include "fsl_reset.h" +#endif + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus*/ + +/*! + * @brief Enable specific interrupt. + * + * Enable LEVEL1 interrupt. For some devices, there might be multiple interrupt + * levels. For example, there are NVIC and intmux. Here the interrupts connected + * to NVIC are the LEVEL1 interrupts, because they are routed to the core directly. + * The interrupts connected to intmux are the LEVEL2 interrupts, they are routed + * to NVIC first then routed to core. + * + * This function only enables the LEVEL1 interrupts. The number of LEVEL1 interrupts + * is indicated by the feature macro FSL_FEATURE_NUMBER_OF_LEVEL1_INT_VECTORS. + * + * @param interrupt The IRQ number. + * @retval kStatus_Success Interrupt enabled successfully + * @retval kStatus_Fail Failed to enable the interrupt + */ +static inline status_t EnableIRQ(IRQn_Type interrupt) +{ + status_t status = kStatus_Success; + + if (NotAvail_IRQn == interrupt) + { + status = kStatus_Fail; + } + +#if defined(FSL_FEATURE_NUMBER_OF_LEVEL1_INT_VECTORS) && (FSL_FEATURE_NUMBER_OF_LEVEL1_INT_VECTORS > 0) + else if ((int32_t)interrupt >= (int32_t)FSL_FEATURE_NUMBER_OF_LEVEL1_INT_VECTORS) + { + status = kStatus_Fail; + } +#endif + + else + { +#if defined(__GIC_PRIO_BITS) + GIC_EnableIRQ(interrupt); +#else + NVIC_EnableIRQ(interrupt); +#endif + } + + return status; +} + +/*! + * @brief Disable specific interrupt. + * + * Disable LEVEL1 interrupt. For some devices, there might be multiple interrupt + * levels. For example, there are NVIC and intmux. Here the interrupts connected + * to NVIC are the LEVEL1 interrupts, because they are routed to the core directly. + * The interrupts connected to intmux are the LEVEL2 interrupts, they are routed + * to NVIC first then routed to core. + * + * This function only disables the LEVEL1 interrupts. The number of LEVEL1 interrupts + * is indicated by the feature macro FSL_FEATURE_NUMBER_OF_LEVEL1_INT_VECTORS. + * + * @param interrupt The IRQ number. + * @retval kStatus_Success Interrupt disabled successfully + * @retval kStatus_Fail Failed to disable the interrupt + */ +static inline status_t DisableIRQ(IRQn_Type interrupt) +{ + status_t status = kStatus_Success; + + if (NotAvail_IRQn == interrupt) + { + status = kStatus_Fail; + } + +#if defined(FSL_FEATURE_NUMBER_OF_LEVEL1_INT_VECTORS) && (FSL_FEATURE_NUMBER_OF_LEVEL1_INT_VECTORS > 0) + else if ((int32_t)interrupt >= (int32_t)FSL_FEATURE_NUMBER_OF_LEVEL1_INT_VECTORS) + { + status = kStatus_Fail; + } +#endif + + else + { +#if defined(__GIC_PRIO_BITS) + GIC_DisableIRQ(interrupt); +#else + NVIC_DisableIRQ(interrupt); +#endif + } + + return status; +} + +/*! + * @brief Disable the global IRQ + * + * Disable the global interrupt and return the current primask register. User is required to provided the primask + * register for the EnableGlobalIRQ(). + * + * @return Current primask value. + */ +static inline uint32_t DisableGlobalIRQ(void) +{ +#if defined(CPSR_I_Msk) + uint32_t cpsr = __get_CPSR() & CPSR_I_Msk; + + __disable_irq(); + + return cpsr; +#else + uint32_t regPrimask = __get_PRIMASK(); + + __disable_irq(); + + return regPrimask; +#endif +} + +/*! + * @brief Enable the global IRQ + * + * Set the primask register with the provided primask value but not just enable the primask. The idea is for the + * convenience of integration of RTOS. some RTOS get its own management mechanism of primask. User is required to + * use the EnableGlobalIRQ() and DisableGlobalIRQ() in pair. + * + * @param primask value of primask register to be restored. The primask value is supposed to be provided by the + * DisableGlobalIRQ(). + */ +static inline void EnableGlobalIRQ(uint32_t primask) +{ +#if defined(CPSR_I_Msk) + __set_CPSR((__get_CPSR() & ~CPSR_I_Msk) | primask); +#else + __set_PRIMASK(primask); +#endif +} + +#if defined(ENABLE_RAM_VECTOR_TABLE) +/*! + * @brief install IRQ handler + * + * @param irq IRQ number + * @param irqHandler IRQ handler address + * @return The old IRQ handler address + */ +uint32_t InstallIRQHandler(IRQn_Type irq, uint32_t irqHandler); +#endif /* ENABLE_RAM_VECTOR_TABLE. */ + +#if (defined(FSL_FEATURE_SOC_SYSCON_COUNT) && (FSL_FEATURE_SOC_SYSCON_COUNT > 0)) + +/* + * When FSL_FEATURE_POWERLIB_EXTEND is defined to non-zero value, + * powerlib should be used instead of these functions. + */ +#if !(defined(FSL_FEATURE_POWERLIB_EXTEND) && (FSL_FEATURE_POWERLIB_EXTEND != 0)) +/*! + * @brief Enable specific interrupt for wake-up from deep-sleep mode. + * + * Enable the interrupt for wake-up from deep sleep mode. + * Some interrupts are typically used in sleep mode only and will not occur during + * deep-sleep mode because relevant clocks are stopped. However, it is possible to enable + * those clocks (significantly increasing power consumption in the reduced power mode), + * making these wake-ups possible. + * + * @note This function also enables the interrupt in the NVIC (EnableIRQ() is called internaly). + * + * @param interrupt The IRQ number. + */ +void EnableDeepSleepIRQ(IRQn_Type interrupt); + +/*! + * @brief Disable specific interrupt for wake-up from deep-sleep mode. + * + * Disable the interrupt for wake-up from deep sleep mode. + * Some interrupts are typically used in sleep mode only and will not occur during + * deep-sleep mode because relevant clocks are stopped. However, it is possible to enable + * those clocks (significantly increasing power consumption in the reduced power mode), + * making these wake-ups possible. + * + * @note This function also disables the interrupt in the NVIC (DisableIRQ() is called internaly). + * + * @param interrupt The IRQ number. + */ +void DisableDeepSleepIRQ(IRQn_Type interrupt); +#endif /* FSL_FEATURE_POWERLIB_EXTEND */ +#endif /* FSL_FEATURE_SOC_SYSCON_COUNT */ + +#if defined(__cplusplus) +} +#endif /* __cplusplus*/ + +/*! @} */ + +#endif /* _FSL_COMMON_ARM_H_ */ diff --git a/drivers/fsl_flexcomm.c b/drivers/fsl_flexcomm.c new file mode 100644 index 0000000..80a3c98 --- /dev/null +++ b/drivers/fsl_flexcomm.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_common.h" +#include "fsl_flexcomm.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.flexcomm" +#endif + +/*! + * @brief Used for conversion between `void*` and `uint32_t`. + */ +typedef union pvoid_to_u32 +{ + void *pvoid; + uint32_t u32; +} pvoid_to_u32_t; + +/******************************************************************************* + * Prototypes + ******************************************************************************/ +/*! @brief Set the FLEXCOMM mode . */ +static status_t FLEXCOMM_SetPeriph(FLEXCOMM_Type *base, FLEXCOMM_PERIPH_T periph, int lock); + +/*! @brief check whether flexcomm supports peripheral type */ +static bool FLEXCOMM_PeripheralIsPresent(FLEXCOMM_Type *base, FLEXCOMM_PERIPH_T periph); + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/*! @brief Array to map FLEXCOMM instance number to base address. */ +static const uint32_t s_flexcommBaseAddrs[] = FLEXCOMM_BASE_ADDRS; + +/*! @brief Pointers to real IRQ handlers installed by drivers for each instance. */ +static flexcomm_irq_handler_t s_flexcommIrqHandler[ARRAY_SIZE(s_flexcommBaseAddrs)]; + +/*! @brief Pointers to handles for each instance to provide context to interrupt routines */ +static void *s_flexcommHandle[ARRAY_SIZE(s_flexcommBaseAddrs)]; + +/*! @brief Array to map FLEXCOMM instance number to IRQ number. */ +IRQn_Type const kFlexcommIrqs[] = FLEXCOMM_IRQS; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) +/*! @brief IDs of clock for each FLEXCOMM module */ +static const clock_ip_name_t s_flexcommClocks[] = FLEXCOMM_CLOCKS; +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +#if !(defined(FSL_FEATURE_FLEXCOMM_HAS_NO_RESET) && FSL_FEATURE_FLEXCOMM_HAS_NO_RESET) +/*! @brief Pointers to FLEXCOMM resets for each instance. */ +static const reset_ip_name_t s_flexcommResets[] = FLEXCOMM_RSTS; +#endif + +/******************************************************************************* + * Code + ******************************************************************************/ + +/* check whether flexcomm supports peripheral type */ +static bool FLEXCOMM_PeripheralIsPresent(FLEXCOMM_Type *base, FLEXCOMM_PERIPH_T periph) +{ + if (periph == FLEXCOMM_PERIPH_NONE) + { + return true; + } + else if (periph <= FLEXCOMM_PERIPH_I2S_TX) + { + return (base->PSELID & (1UL << ((uint32_t)periph + 3U))) > 0UL ? true : false; + } + else if (periph == FLEXCOMM_PERIPH_I2S_RX) + { + return (base->PSELID & (1U << 7U)) > (uint32_t)0U ? true : false; + } + else + { + return false; + } +} + +/* Get the index corresponding to the FLEXCOMM */ +/*! brief Returns instance number for FLEXCOMM module with given base address. */ +uint32_t FLEXCOMM_GetInstance(void *base) +{ + uint32_t i; + pvoid_to_u32_t BaseAddr; + BaseAddr.pvoid = base; + + for (i = 0U; i < (uint32_t)FSL_FEATURE_SOC_FLEXCOMM_COUNT; i++) + { + if (BaseAddr.u32 == s_flexcommBaseAddrs[i]) + { + break; + } + } + + assert(i < (uint32_t)FSL_FEATURE_SOC_FLEXCOMM_COUNT); + return i; +} + +/* Changes FLEXCOMM mode */ +static status_t FLEXCOMM_SetPeriph(FLEXCOMM_Type *base, FLEXCOMM_PERIPH_T periph, int lock) +{ + /* Check whether peripheral type is present */ + if (!FLEXCOMM_PeripheralIsPresent(base, periph)) + { + return kStatus_OutOfRange; + } + + /* Flexcomm is locked to different peripheral type than expected */ + if (((base->PSELID & FLEXCOMM_PSELID_LOCK_MASK) != 0U) && + ((base->PSELID & FLEXCOMM_PSELID_PERSEL_MASK) != (uint32_t)periph)) + { + return kStatus_Fail; + } + + /* Check if we are asked to lock */ + if (lock != 0) + { + base->PSELID = (uint32_t)periph | FLEXCOMM_PSELID_LOCK_MASK; + } + else + { + base->PSELID = (uint32_t)periph; + } + + return kStatus_Success; +} + +/*! brief Initializes FLEXCOMM and selects peripheral mode according to the second parameter. */ +status_t FLEXCOMM_Init(void *base, FLEXCOMM_PERIPH_T periph) +{ + uint32_t idx = FLEXCOMM_GetInstance(base); + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + /* Enable the peripheral clock */ + CLOCK_EnableClock(s_flexcommClocks[idx]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +#if !(defined(FSL_FEATURE_FLEXCOMM_HAS_NO_RESET) && FSL_FEATURE_FLEXCOMM_HAS_NO_RESET) + /* Reset the FLEXCOMM module */ + RESET_PeripheralReset(s_flexcommResets[idx]); +#endif + + /* Set the FLEXCOMM to given peripheral */ + return FLEXCOMM_SetPeriph((FLEXCOMM_Type *)base, periph, 0); +} + +/*! brief Sets IRQ handler for given FLEXCOMM module. It is used by drivers register IRQ handler according to FLEXCOMM + * mode */ +void FLEXCOMM_SetIRQHandler(void *base, flexcomm_irq_handler_t handler, void *flexcommHandle) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(base); + + /* Clear handler first to avoid execution of the handler with wrong handle */ + s_flexcommIrqHandler[instance] = NULL; + s_flexcommHandle[instance] = flexcommHandle; + s_flexcommIrqHandler[instance] = handler; + SDK_ISR_EXIT_BARRIER; +} + +/* IRQ handler functions overloading weak symbols in the startup */ +#if defined(FLEXCOMM0) +void FLEXCOMM0_DriverIRQHandler(void); +void FLEXCOMM0_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM0); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM1) +void FLEXCOMM1_DriverIRQHandler(void); +void FLEXCOMM1_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM1); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM2) +void FLEXCOMM2_DriverIRQHandler(void); +void FLEXCOMM2_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM2); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM3) +void FLEXCOMM3_DriverIRQHandler(void); +void FLEXCOMM3_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM3); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM4) +void FLEXCOMM4_DriverIRQHandler(void); +void FLEXCOMM4_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM4); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} + +#endif + +#if defined(FLEXCOMM5) +void FLEXCOMM5_DriverIRQHandler(void); +void FLEXCOMM5_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM5); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM6) +void FLEXCOMM6_DriverIRQHandler(void); +void FLEXCOMM6_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM6); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM7) +void FLEXCOMM7_DriverIRQHandler(void); +void FLEXCOMM7_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM7); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM8) +void FLEXCOMM8_DriverIRQHandler(void); +void FLEXCOMM8_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM8); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM9) +void FLEXCOMM9_DriverIRQHandler(void); +void FLEXCOMM9_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM9); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM10) +void FLEXCOMM10_DriverIRQHandler(void); +void FLEXCOMM10_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM10); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM11) +void FLEXCOMM11_DriverIRQHandler(void); +void FLEXCOMM11_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM11); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM12) +void FLEXCOMM12_DriverIRQHandler(void); +void FLEXCOMM12_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM12); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM13) +void FLEXCOMM13_DriverIRQHandler(void); +void FLEXCOMM13_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM13); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM14) +void FLEXCOMM14_DriverIRQHandler(void); +void FLEXCOMM14_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM14); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM15) +void FLEXCOMM15_DriverIRQHandler(void); +void FLEXCOMM15_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM15); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FLEXCOMM16) +void FLEXCOMM16_DriverIRQHandler(void); +void FLEXCOMM16_DriverIRQHandler(void) +{ + uint32_t instance; + + /* Look up instance number */ + instance = FLEXCOMM_GetInstance(FLEXCOMM16); + assert(s_flexcommIrqHandler[instance] != NULL); + s_flexcommIrqHandler[instance]((uint32_t *)s_flexcommBaseAddrs[instance], s_flexcommHandle[instance]); + SDK_ISR_EXIT_BARRIER; +} +#endif diff --git a/drivers/fsl_flexcomm.h b/drivers/fsl_flexcomm.h new file mode 100644 index 0000000..7fb652b --- /dev/null +++ b/drivers/fsl_flexcomm.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_FLEXCOMM_H_ +#define _FSL_FLEXCOMM_H_ + +#include "fsl_common.h" + +/*! + * @addtogroup flexcomm_driver + * @{ + */ + +/*! @name Driver version */ +/*@{*/ +/*! @brief FlexCOMM driver version 2.0.2. */ +#define FSL_FLEXCOMM_DRIVER_VERSION (MAKE_VERSION(2, 0, 2)) +/*@}*/ + +/*! @brief FLEXCOMM peripheral modes. */ +typedef enum +{ + FLEXCOMM_PERIPH_NONE, /*!< No peripheral */ + FLEXCOMM_PERIPH_USART, /*!< USART peripheral */ + FLEXCOMM_PERIPH_SPI, /*!< SPI Peripheral */ + FLEXCOMM_PERIPH_I2C, /*!< I2C Peripheral */ + FLEXCOMM_PERIPH_I2S_TX, /*!< I2S TX Peripheral */ + FLEXCOMM_PERIPH_I2S_RX, /*!< I2S RX Peripheral */ +} FLEXCOMM_PERIPH_T; + +/*! @brief Typedef for interrupt handler. */ +typedef void (*flexcomm_irq_handler_t)(void *base, void *handle); + +/*! @brief Array with IRQ number for each FLEXCOMM module. */ +extern IRQn_Type const kFlexcommIrqs[]; + +/******************************************************************************* + * API + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif + +/*! @brief Returns instance number for FLEXCOMM module with given base address. */ +uint32_t FLEXCOMM_GetInstance(void *base); + +/*! @brief Initializes FLEXCOMM and selects peripheral mode according to the second parameter. */ +status_t FLEXCOMM_Init(void *base, FLEXCOMM_PERIPH_T periph); + +/*! @brief Sets IRQ handler for given FLEXCOMM module. It is used by drivers register IRQ handler according to FLEXCOMM + * mode */ +void FLEXCOMM_SetIRQHandler(void *base, flexcomm_irq_handler_t handler, void *flexcommHandle); + +#if defined(__cplusplus) +} +#endif + +/*@}*/ + +#endif /* _FSL_FLEXCOMM_H_*/ diff --git a/drivers/fsl_gpio.c b/drivers/fsl_gpio.c new file mode 100644 index 0000000..3a0cdd3 --- /dev/null +++ b/drivers/fsl_gpio.c @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_gpio.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.lpc_gpio" +#endif + +/******************************************************************************* + * Variables + ******************************************************************************/ +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) +/*! @brief Array to map FGPIO instance number to clock name. */ +static const clock_ip_name_t s_gpioClockName[] = GPIO_CLOCKS; +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +#if !(defined(FSL_FEATURE_GPIO_HAS_NO_RESET) && FSL_FEATURE_GPIO_HAS_NO_RESET) +/*! @brief Pointers to GPIO resets for each instance. */ +static const reset_ip_name_t s_gpioResets[] = GPIO_RSTS_N; +#endif +/******************************************************************************* + * Prototypes + ************ ******************************************************************/ +/*! + * @brief Enable GPIO port clock. + * + * @param base GPIO peripheral base pointer. + * @param port GPIO port number. + */ +static void GPIO_EnablePortClock(GPIO_Type *base, uint32_t port); + +/******************************************************************************* + * Code + ******************************************************************************/ +static void GPIO_EnablePortClock(GPIO_Type *base, uint32_t port) +{ +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + assert(port < ARRAY_SIZE(s_gpioClockName)); + + /* Upgate the GPIO clock */ + CLOCK_EnableClock(s_gpioClockName[port]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ +} + +/*! + * brief Initializes the GPIO peripheral. + * + * This function ungates the GPIO clock. + * + * param base GPIO peripheral base pointer. + * param port GPIO port number. + */ +void GPIO_PortInit(GPIO_Type *base, uint32_t port) +{ + GPIO_EnablePortClock(base, port); + +#if !(defined(FSL_FEATURE_GPIO_HAS_NO_RESET) && FSL_FEATURE_GPIO_HAS_NO_RESET) + /* Reset the GPIO module */ + RESET_PeripheralReset(s_gpioResets[port]); +#endif +} + +/*! + * brief Initializes a GPIO pin used by the board. + * + * To initialize the GPIO, define a pin configuration, either input or output, in the user file. + * Then, call the GPIO_PinInit() function. + * + * This is an example to define an input pin or output pin configuration: + * code + * Define a digital input pin configuration, + * gpio_pin_config_t config = + * { + * kGPIO_DigitalInput, + * 0, + * } + * Define a digital output pin configuration, + * gpio_pin_config_t config = + * { + * kGPIO_DigitalOutput, + * 0, + * } + * endcode + * + * param base GPIO peripheral base pointer(Typically GPIO) + * param port GPIO port number + * param pin GPIO pin number + * param config GPIO pin configuration pointer + */ +void GPIO_PinInit(GPIO_Type *base, uint32_t port, uint32_t pin, const gpio_pin_config_t *config) +{ + GPIO_EnablePortClock(base, port); + + if (config->pinDirection == kGPIO_DigitalInput) + { +#if defined(FSL_FEATURE_GPIO_DIRSET_AND_DIRCLR) && (FSL_FEATURE_GPIO_DIRSET_AND_DIRCLR) + base->DIRCLR[port] = 1UL << pin; +#else + base->DIR[port] &= ~(1UL << pin); +#endif /*FSL_FEATURE_GPIO_DIRSET_AND_DIRCLR*/ + } + else + { + /* Set default output value */ + if (config->outputLogic == 0U) + { + base->CLR[port] = (1UL << pin); + } + else + { + base->SET[port] = (1UL << pin); + } +/* Set pin direction */ +#if defined(FSL_FEATURE_GPIO_DIRSET_AND_DIRCLR) && (FSL_FEATURE_GPIO_DIRSET_AND_DIRCLR) + base->DIRSET[port] = 1UL << pin; +#else + base->DIR[port] |= 1UL << pin; +#endif /*FSL_FEATURE_GPIO_DIRSET_AND_DIRCLR*/ + } +} + +#if defined(FSL_FEATURE_GPIO_HAS_INTERRUPT) && FSL_FEATURE_GPIO_HAS_INTERRUPT +/*! + * @brief Set the configuration of pin interrupt. + * + * @param base GPIO base pointer. + * @param port GPIO port number + * @param pin GPIO pin number. + * @param config GPIO pin interrupt configuration.. + */ +void GPIO_SetPinInterruptConfig(GPIO_Type *base, uint32_t port, uint32_t pin, gpio_interrupt_config_t *config) +{ + base->INTEDG[port] = (base->INTEDG[port] & ~(1UL << pin)) | ((uint32_t)config->mode << pin); + + base->INTPOL[port] = (base->INTPOL[port] & ~(1UL << pin)) | ((uint32_t)config->polarity << pin); +} + +/*! + * @brief Enables multiple pins interrupt. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param index GPIO interrupt number. + * @param mask GPIO pin number macro. + */ +void GPIO_PortEnableInterrupts(GPIO_Type *base, uint32_t port, uint32_t index, uint32_t mask) +{ + if ((uint32_t)kGPIO_InterruptA == index) + { + base->INTENA[port] = base->INTENA[port] | mask; + } + else if ((uint32_t)kGPIO_InterruptB == index) + { + base->INTENB[port] = base->INTENB[port] | mask; + } + else + { + /*Should not enter here*/ + } +} + +/*! + * @brief Disables multiple pins interrupt. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param index GPIO interrupt number. + * @param mask GPIO pin number macro. + */ +void GPIO_PortDisableInterrupts(GPIO_Type *base, uint32_t port, uint32_t index, uint32_t mask) +{ + if ((uint32_t)kGPIO_InterruptA == index) + { + base->INTENA[port] = base->INTENA[port] & ~mask; + } + else if ((uint32_t)kGPIO_InterruptB == index) + { + base->INTENB[port] = base->INTENB[port] & ~mask; + } + else + { + /*Should not enter here*/ + } +} + +/*! + * @brief Clears multiple pins interrupt flag. Status flags are cleared by + * writing a 1 to the corresponding bit position. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param index GPIO interrupt number. + * @param mask GPIO pin number macro. + */ +void GPIO_PortClearInterruptFlags(GPIO_Type *base, uint32_t port, uint32_t index, uint32_t mask) +{ + if ((uint32_t)kGPIO_InterruptA == index) + { + base->INTSTATA[port] = mask; + } + else if ((uint32_t)kGPIO_InterruptB == index) + { + base->INTSTATB[port] = mask; + } + else + { + /*Should not enter here*/ + } +} + +/*! + * @ Read port interrupt status. + * + * @param base GPIO base pointer. + * @param port GPIO port number + * @param index GPIO interrupt number. + * @retval masked GPIO status value + */ +uint32_t GPIO_PortGetInterruptStatus(GPIO_Type *base, uint32_t port, uint32_t index) +{ + uint32_t status = 0U; + + if ((uint32_t)kGPIO_InterruptA == index) + { + status = base->INTSTATA[port]; + } + else if ((uint32_t)kGPIO_InterruptB == index) + { + status = base->INTSTATB[port]; + } + else + { + /*Should not enter here*/ + } + return status; +} + +/*! + * @brief Enables the specific pin interrupt. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param pin GPIO pin number. + * @param index GPIO interrupt number. + */ +void GPIO_PinEnableInterrupt(GPIO_Type *base, uint32_t port, uint32_t pin, uint32_t index) +{ + if ((uint32_t)kGPIO_InterruptA == index) + { + base->INTENA[port] = base->INTENA[port] | (1UL << pin); + } + else if ((uint32_t)kGPIO_InterruptB == index) + { + base->INTENB[port] = base->INTENB[port] | (1UL << pin); + } + else + { + /*Should not enter here*/ + } +} + +/*! + * @brief Disables the specific pin interrupt. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param pin GPIO pin number. + * @param index GPIO interrupt number. + */ +void GPIO_PinDisableInterrupt(GPIO_Type *base, uint32_t port, uint32_t pin, uint32_t index) +{ + if ((uint32_t)kGPIO_InterruptA == index) + { + base->INTENA[port] = base->INTENA[port] & ~(1UL << pin); + } + else if ((uint32_t)kGPIO_InterruptB == index) + { + base->INTENB[port] = base->INTENB[port] & ~(1UL << pin); + } + else + { + /*Should not enter here*/ + } +} + +/*! + * @brief Clears the specific pin interrupt flag. Status flags are cleared by + * writing a 1 to the corresponding bit position. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param index GPIO interrupt number. + * @param mask GPIO pin number macro. + */ +void GPIO_PinClearInterruptFlag(GPIO_Type *base, uint32_t port, uint32_t pin, uint32_t index) +{ + if ((uint32_t)kGPIO_InterruptA == index) + { + base->INTSTATA[port] = 1UL << pin; + } + else if ((uint32_t)kGPIO_InterruptB == index) + { + base->INTSTATB[port] = 1UL << pin; + } + else + { + /*Should not enter here*/ + } +} +#endif /* FSL_FEATURE_GPIO_HAS_INTERRUPT */ diff --git a/drivers/fsl_gpio.h b/drivers/fsl_gpio.h new file mode 100644 index 0000000..f746eba --- /dev/null +++ b/drivers/fsl_gpio.h @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _LPC_GPIO_H_ +#define _LPC_GPIO_H_ + +#include "fsl_common.h" + +/*! + * @addtogroup lpc_gpio + * @{ + */ + +/*! @file */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +/*! @brief LPC GPIO driver version. */ +#define FSL_GPIO_DRIVER_VERSION (MAKE_VERSION(2, 1, 7)) +/*@}*/ + +/*! @brief LPC GPIO direction definition */ +typedef enum _gpio_pin_direction +{ + kGPIO_DigitalInput = 0U, /*!< Set current pin as digital input*/ + kGPIO_DigitalOutput = 1U, /*!< Set current pin as digital output*/ +} gpio_pin_direction_t; + +/*! + * @brief The GPIO pin configuration structure. + * + * Every pin can only be configured as either output pin or input pin at a time. + * If configured as a input pin, then leave the outputConfig unused. + */ +typedef struct _gpio_pin_config +{ + gpio_pin_direction_t pinDirection; /*!< GPIO direction, input or output */ + /* Output configurations, please ignore if configured as a input one */ + uint8_t outputLogic; /*!< Set default output logic, no use in input */ +} gpio_pin_config_t; + +#if (defined(FSL_FEATURE_GPIO_HAS_INTERRUPT) && FSL_FEATURE_GPIO_HAS_INTERRUPT) +#define GPIO_PIN_INT_LEVEL 0x00U +#define GPIO_PIN_INT_EDGE 0x01U + +#define PINT_PIN_INT_HIGH_OR_RISE_TRIGGER 0x00U +#define PINT_PIN_INT_LOW_OR_FALL_TRIGGER 0x01U + +/*! @brief GPIO Pin Interrupt enable mode */ +typedef enum _gpio_pin_enable_mode +{ + kGPIO_PinIntEnableLevel = GPIO_PIN_INT_LEVEL, /*!< Generate Pin Interrupt on level mode */ + kGPIO_PinIntEnableEdge = GPIO_PIN_INT_EDGE /*!< Generate Pin Interrupt on edge mode */ +} gpio_pin_enable_mode_t; + +/*! @brief GPIO Pin Interrupt enable polarity */ +typedef enum _gpio_pin_enable_polarity +{ + kGPIO_PinIntEnableHighOrRise = + PINT_PIN_INT_HIGH_OR_RISE_TRIGGER, /*!< Generate Pin Interrupt on high level or rising edge */ + kGPIO_PinIntEnableLowOrFall = + PINT_PIN_INT_LOW_OR_FALL_TRIGGER /*!< Generate Pin Interrupt on low level or falling edge */ +} gpio_pin_enable_polarity_t; + +/*! @brief LPC GPIO interrupt index definition */ +typedef enum _gpio_interrupt_index +{ + kGPIO_InterruptA = 0U, /*!< Set current pin as interrupt A*/ + kGPIO_InterruptB = 1U, /*!< Set current pin as interrupt B*/ +} gpio_interrupt_index_t; + +/*! @brief Configures the interrupt generation condition. */ +typedef struct _gpio_interrupt_config +{ + uint8_t mode; /* The trigger mode of GPIO interrupts */ + uint8_t polarity; /* The polarity of GPIO interrupts */ +} gpio_interrupt_config_t; +#endif + +/******************************************************************************* + * API + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif + +/*! @name GPIO Configuration */ +/*@{*/ + +/*! + * @brief Initializes the GPIO peripheral. + * + * This function ungates the GPIO clock. + * + * @param base GPIO peripheral base pointer. + * @param port GPIO port number. + */ +void GPIO_PortInit(GPIO_Type *base, uint32_t port); + +/*! + * @brief Initializes a GPIO pin used by the board. + * + * To initialize the GPIO, define a pin configuration, either input or output, in the user file. + * Then, call the GPIO_PinInit() function. + * + * This is an example to define an input pin or output pin configuration: + * @code + * Define a digital input pin configuration, + * gpio_pin_config_t config = + * { + * kGPIO_DigitalInput, + * 0, + * } + * Define a digital output pin configuration, + * gpio_pin_config_t config = + * { + * kGPIO_DigitalOutput, + * 0, + * } + * @endcode + * + * @param base GPIO peripheral base pointer(Typically GPIO) + * @param port GPIO port number + * @param pin GPIO pin number + * @param config GPIO pin configuration pointer + */ +void GPIO_PinInit(GPIO_Type *base, uint32_t port, uint32_t pin, const gpio_pin_config_t *config); + +/*@}*/ + +/*! @name GPIO Output Operations */ +/*@{*/ + +/*! + * @brief Sets the output level of the one GPIO pin to the logic 1 or 0. + * + * @param base GPIO peripheral base pointer(Typically GPIO) + * @param port GPIO port number + * @param pin GPIO pin number + * @param output GPIO pin output logic level. + * - 0: corresponding pin output low-logic level. + * - 1: corresponding pin output high-logic level. + */ +static inline void GPIO_PinWrite(GPIO_Type *base, uint32_t port, uint32_t pin, uint8_t output) +{ + base->B[port][pin] = output; +} + +/*@}*/ +/*! @name GPIO Input Operations */ +/*@{*/ + +/*! + * @brief Reads the current input value of the GPIO PIN. + * + * @param base GPIO peripheral base pointer(Typically GPIO) + * @param port GPIO port number + * @param pin GPIO pin number + * @retval GPIO port input value + * - 0: corresponding pin input low-logic level. + * - 1: corresponding pin input high-logic level. + */ +static inline uint32_t GPIO_PinRead(GPIO_Type *base, uint32_t port, uint32_t pin) +{ + return (uint32_t)base->B[port][pin]; +} + +/*@}*/ + +/*! + * @brief Sets the output level of the multiple GPIO pins to the logic 1. + * + * @param base GPIO peripheral base pointer(Typically GPIO) + * @param port GPIO port number + * @param mask GPIO pin number macro + */ +static inline void GPIO_PortSet(GPIO_Type *base, uint32_t port, uint32_t mask) +{ + base->SET[port] = mask; +} + +/*! + * @brief Sets the output level of the multiple GPIO pins to the logic 0. + * + * @param base GPIO peripheral base pointer(Typically GPIO) + * @param port GPIO port number + * @param mask GPIO pin number macro + */ +static inline void GPIO_PortClear(GPIO_Type *base, uint32_t port, uint32_t mask) +{ + base->CLR[port] = mask; +} + +/*! + * @brief Reverses current output logic of the multiple GPIO pins. + * + * @param base GPIO peripheral base pointer(Typically GPIO) + * @param port GPIO port number + * @param mask GPIO pin number macro + */ +static inline void GPIO_PortToggle(GPIO_Type *base, uint32_t port, uint32_t mask) +{ + base->NOT[port] = mask; +} + +/*@}*/ + +/*! + * @brief Reads the current input value of the whole GPIO port. + * + * @param base GPIO peripheral base pointer(Typically GPIO) + * @param port GPIO port number + */ +static inline uint32_t GPIO_PortRead(GPIO_Type *base, uint32_t port) +{ + return (uint32_t)base->PIN[port]; +} + +/*@}*/ +/*! @name GPIO Mask Operations */ +/*@{*/ + +/*! + * @brief Sets port mask, 0 - enable pin, 1 - disable pin. + * + * @param base GPIO peripheral base pointer(Typically GPIO) + * @param port GPIO port number + * @param mask GPIO pin number macro + */ +static inline void GPIO_PortMaskedSet(GPIO_Type *base, uint32_t port, uint32_t mask) +{ + base->MASK[port] = mask; +} + +/*! + * @brief Sets the output level of the masked GPIO port. Only pins enabled by GPIO_SetPortMask() will be affected. + * + * @param base GPIO peripheral base pointer(Typically GPIO) + * @param port GPIO port number + * @param output GPIO port output value. + */ +static inline void GPIO_PortMaskedWrite(GPIO_Type *base, uint32_t port, uint32_t output) +{ + base->MPIN[port] = output; +} + +/*! + * @brief Reads the current input value of the masked GPIO port. Only pins enabled by GPIO_SetPortMask() will be + * affected. + * + * @param base GPIO peripheral base pointer(Typically GPIO) + * @param port GPIO port number + * @retval masked GPIO port value + */ +static inline uint32_t GPIO_PortMaskedRead(GPIO_Type *base, uint32_t port) +{ + return (uint32_t)base->MPIN[port]; +} + +#if defined(FSL_FEATURE_GPIO_HAS_INTERRUPT) && FSL_FEATURE_GPIO_HAS_INTERRUPT +/*! + * @brief Set the configuration of pin interrupt. + * + * @param base GPIO base pointer. + * @param port GPIO port number + * @param pin GPIO pin number. + * @param config GPIO pin interrupt configuration.. + */ +void GPIO_SetPinInterruptConfig(GPIO_Type *base, uint32_t port, uint32_t pin, gpio_interrupt_config_t *config); + +/*! + * @brief Enables multiple pins interrupt. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param index GPIO interrupt number. + * @param mask GPIO pin number macro. + */ +void GPIO_PortEnableInterrupts(GPIO_Type *base, uint32_t port, uint32_t index, uint32_t mask); + +/*! + * @brief Disables multiple pins interrupt. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param index GPIO interrupt number. + * @param mask GPIO pin number macro. + */ +void GPIO_PortDisableInterrupts(GPIO_Type *base, uint32_t port, uint32_t index, uint32_t mask); + +/*! + * @brief Clears pin interrupt flag. Status flags are cleared by + * writing a 1 to the corresponding bit position. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param index GPIO interrupt number. + * @param mask GPIO pin number macro. + */ +void GPIO_PortClearInterruptFlags(GPIO_Type *base, uint32_t port, uint32_t index, uint32_t mask); + +/*! + * @ Read port interrupt status. + * + * @param base GPIO base pointer. + * @param port GPIO port number + * @param index GPIO interrupt number. + * @retval masked GPIO status value + */ +uint32_t GPIO_PortGetInterruptStatus(GPIO_Type *base, uint32_t port, uint32_t index); + +/*! + * @brief Enables the specific pin interrupt. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param pin GPIO pin number. + * @param index GPIO interrupt number. + */ +void GPIO_PinEnableInterrupt(GPIO_Type *base, uint32_t port, uint32_t pin, uint32_t index); + +/*! + * @brief Disables the specific pin interrupt. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param pin GPIO pin number. + * @param index GPIO interrupt number. + */ +void GPIO_PinDisableInterrupt(GPIO_Type *base, uint32_t port, uint32_t pin, uint32_t index); + +/*! + * @brief Clears the specific pin interrupt flag. Status flags are cleared by + * writing a 1 to the corresponding bit position. + * + * @param base GPIO base pointer. + * @param port GPIO port number. + * @param pin GPIO pin number. + * @param index GPIO interrupt number. + */ +void GPIO_PinClearInterruptFlag(GPIO_Type *base, uint32_t port, uint32_t pin, uint32_t index); + +#endif /* FSL_FEATURE_GPIO_HAS_INTERRUPT */ + +/*@}*/ + +#if defined(__cplusplus) +} +#endif + +/*! + * @} + */ + +#endif /* _LPC_GPIO_H_*/ diff --git a/drivers/fsl_i2c.c b/drivers/fsl_i2c.c new file mode 100644 index 0000000..9f02622 --- /dev/null +++ b/drivers/fsl_i2c.c @@ -0,0 +1,2085 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_i2c.h" +#include "fsl_flexcomm.h" +#include +#include + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.flexcomm_i2c" +#endif + +/*! @brief Common sets of flags used by the driver's transactional layer internally. */ +enum _i2c_flag_constants +{ + kI2C_MasterIrqFlags = I2C_INTSTAT_MSTPENDING_MASK | I2C_INTSTAT_MSTARBLOSS_MASK | I2C_INTSTAT_MSTSTSTPERR_MASK | + I2C_INTSTAT_EVENTTIMEOUT_MASK | I2C_INTSTAT_SCLTIMEOUT_MASK, + kI2C_SlaveIrqFlags = I2C_INTSTAT_SLVPENDING_MASK | I2C_INTSTAT_SLVDESEL_MASK, +}; + +/*! + * @brief Used for conversion from `flexcomm_irq_handler_t` to `flexcomm_i2c_master_irq_handler_t` and + * `flexcomm_i2c_slave_irq_handler_t`. + */ +typedef union i2c_to_flexcomm +{ + flexcomm_i2c_master_irq_handler_t i2c_master_handler; + flexcomm_i2c_slave_irq_handler_t i2c_slave_handler; + flexcomm_irq_handler_t flexcomm_handler; +} i2c_to_flexcomm_t; + +/******************************************************************************* + * Prototypes + ******************************************************************************/ +/*! + * @brief Waits for Master Pending status bit to set and check for bus error status. + * + * @param base The I2C peripheral base address. + * @return Bus status. + */ +static status_t I2C_PendingStatusWait(I2C_Type *base); + +/*! + * @brief Prepares the transfer state machine and fills in the command buffer. + * @param base The I2C peripheral base address. + * @param handle Master nonblocking driver handle. + * @param xfer The I2C transfer configuration structure. + */ +static status_t I2C_InitTransferStateMachine(I2C_Type *base, i2c_master_handle_t *handle, i2c_master_transfer_t *xfer); + +/*! + * @brief Resets the slave hardware state machine. + * According to documentation, after disabling slave to rest the slave hardware state machine, the register + * configuration remains unchanged. + * @param base The I2C peripheral base address. + */ +static void I2C_SlaveInternalStateMachineReset(I2C_Type *base); + +/*! + * @brief Compute CLKDIV + * + * This function computes CLKDIV value according to the given bus speed and Flexcomm source clock frequency. + * This setting is used by hardware during slave clock stretching. + * + * @param base The I2C peripheral base address. + * @return status of the operation + */ +static status_t I2C_SlaveDivVal(uint32_t srcClock_Hz, i2c_slave_bus_speed_t busSpeed, uint32_t *divVal); + +/*! + * @brief Poll wait for the SLVPENDING flag. + * + * Wait for the pending status to be set (SLVPENDING = 1) by polling the STAT register. + * + * @param base The I2C peripheral base address. + * @return status register at time the SLVPENDING bit is read as set + */ +static uint32_t I2C_SlavePollPending(I2C_Type *base); + +/*! + * @brief Invoke event from I2C_SlaveTransferHandleIRQ(). + * + * Sets the event type to transfer structure and invokes the event callback, if it has been + * enabled by eventMask. + * + * @param base The I2C peripheral base address. + * @param handle The I2C slave handle for non-blocking APIs. + * @param event The I2C slave event to invoke. + */ +static void I2C_SlaveInvokeEvent(I2C_Type *base, i2c_slave_handle_t *handle, i2c_slave_transfer_event_t event); + +/*! + * @brief Handle slave address match event. + * + * Called by Slave interrupt routine to ACK or NACK the matched address. + * It also determines master direction (read or write). + * + * @param base The I2C peripheral base address. + * @return true if the matched address is ACK'ed + * @return false if the matched address is NACK'ed + */ +static bool I2C_SlaveAddressIRQ(I2C_Type *base, i2c_slave_handle_t *handle); + +/*! + * @brief Starts accepting slave transfers. + * + * Call this API after calling I2C_SlaveInit() and I2C_SlaveTransferCreateHandle() to start processing + * transactions driven by an I2C master. The slave monitors the I2C bus and pass events to the + * callback that was passed into the call to I2C_SlaveTransferCreateHandle(). The callback is always invoked + * from the interrupt context. + * + * @param base The I2C peripheral base address. + * @param handle Pointer to #i2c_slave_handle_t structure which stores the transfer state. + * @param txData Data to be transmitted to master in response to master read from slave requests. NULL if slave RX only. + * @param txSize Size of txData buffer in bytes. + * @param rxData Data where received data from master will be stored in response to master write to slave requests. NULL + * if slave TX only. + * @param rxSize Size of rxData buffer in bytes. + * @retval #kStatus_Success Slave transfers were successfully started. + * @retval #kStatus_I2C_Busy Slave transfers have already been started on this handle. + */ +static status_t I2C_SlaveTransferNonBlockingInternal(I2C_Type *base, + i2c_slave_handle_t *handle, + const void *txData, + size_t txSize, + void *rxData, + size_t rxSize, + uint32_t eventMask); + +/*! + * @brief Execute master transfer software state machine until FIFOs are exhausted. + * + * For master transmit, the states would be kStartState->kTransmitSubaddrState->kTransmitDataState->kStopState + * For master receive, the states would be kStartState->kTransmitSubaddrState->kStartState->kReceiveDataState-> + * kWaitForCompletionState + * + * @param handle Master nonblocking driver handle. + * @param[out] isDone Set to true if the transfer has completed. + * @retval #kStatus_Success + * @retval #kStatus_I2C_ArbitrationLost + * @retval #kStatus_I2C_Nak + */ +static status_t I2C_RunTransferStateMachine(I2C_Type *base, i2c_master_handle_t *handle, bool *isDone); + +/*! + * @brief Checks the slave response to master's start signal. + * + * @param base I2C peripheral base address. + * @retval kStatus_Success Successfully complete the data transmission. + * @retval kStatus_I2C_Timeout Transfer error, wait signal timeout. + * @retval kStataus_I2C_Nak Transfer error, receive NAK during addressing. + */ +static status_t I2C_MasterCheckStartResponse(I2C_Type *base); +/******************************************************************************* + * Variables + ******************************************************************************/ + +/*! @brief Array to map i2c instance number to base address. */ +static const uint32_t s_i2cBaseAddrs[FSL_FEATURE_SOC_I2C_COUNT] = I2C_BASE_ADDRS; + +/*! @brief IRQ name array */ +static const IRQn_Type s_i2cIRQ[] = I2C_IRQS; + +/******************************************************************************* + * Code + ******************************************************************************/ + +/*! + * brief Returns an instance number given a base address. + * + * If an invalid base address is passed, debug builds will assert. Release builds will just return + * instance number 0. + * + * param base The I2C peripheral base address. + * return I2C instance number starting from 0. + */ +uint32_t I2C_GetInstance(I2C_Type *base) +{ + uint32_t i; + for (i = 0; i < (uint32_t)FSL_FEATURE_SOC_I2C_COUNT; i++) + { + if ((uint32_t)base == s_i2cBaseAddrs[i]) + { + break; + } + } + assert(i < (uint32_t)FSL_FEATURE_SOC_I2C_COUNT); + return i; +} + +/*! + * brief Provides a default configuration for the I2C master peripheral. + * + * This function provides the following default configuration for the I2C master peripheral: + * code + * masterConfig->enableMaster = true; + * masterConfig->baudRate_Bps = 100000U; + * masterConfig->enableTimeout = false; + * endcode + * + * After calling this function, you can override any settings in order to customize the configuration, + * prior to initializing the master driver with I2C_MasterInit(). + * + * param[out] masterConfig User provided configuration structure for default values. Refer to #i2c_master_config_t. + */ +void I2C_MasterGetDefaultConfig(i2c_master_config_t *masterConfig) +{ + /* Initializes the configure structure to zero. */ + (void)memset(masterConfig, 0, sizeof(*masterConfig)); + + masterConfig->enableMaster = true; + masterConfig->baudRate_Bps = 100000U; + masterConfig->enableTimeout = false; + masterConfig->timeout_Ms = 35; +} + +/*! + * brief Initializes the I2C master peripheral. + * + * This function enables the peripheral clock and initializes the I2C master peripheral as described by the user + * provided configuration. A software reset is performed prior to configuration. + * + * param base The I2C peripheral base address. + * param masterConfig User provided peripheral configuration. Use I2C_MasterGetDefaultConfig() to get a set of + * defaults + * that you can override. + * param srcClock_Hz Frequency in Hertz of the I2C functional clock. Used to calculate the baud rate divisors, + * filter widths, and timeout periods. + */ +void I2C_MasterInit(I2C_Type *base, const i2c_master_config_t *masterConfig, uint32_t srcClock_Hz) +{ + (void)FLEXCOMM_Init(base, FLEXCOMM_PERIPH_I2C); + I2C_MasterEnable(base, masterConfig->enableMaster); + I2C_MasterSetBaudRate(base, masterConfig->baudRate_Bps, srcClock_Hz); + I2C_MasterSetTimeoutValue(base, masterConfig->timeout_Ms, srcClock_Hz); +} + +/*! + * brief Deinitializes the I2C master peripheral. + * + * This function disables the I2C master peripheral and gates the clock. It also performs a software + * reset to restore the peripheral to reset conditions. + * + * param base The I2C peripheral base address. + */ +void I2C_MasterDeinit(I2C_Type *base) +{ + I2C_MasterEnable(base, false); +} + +/*! + * brief Gets the I2C status flags. + * + * A bit mask with the state of all I2C status flags is returned. For each flag, the corresponding bit + * in the return value is set if the flag is asserted. + * + * param base The I2C peripheral base address. + * return State of the status flags: + * - 1: related status flag is set. + * - 0: related status flag is not set. + * see ref _i2c_status_flags, ref _i2c_master_status_flags and ref _i2c_slave_status_flags. + */ +uint32_t I2C_GetStatusFlags(I2C_Type *base) +{ + uint32_t statusMask = base->STAT; + if ((statusMask & (uint32_t)I2C_STAT_MSTSTATE_MASK) == 0UL) + { + statusMask |= (uint32_t)kI2C_MasterIdleFlag; + } + if (((statusMask & (uint32_t)I2C_STAT_MSTSTATE_MASK) >> I2C_STAT_MSTSTATE_SHIFT) == 3UL) + { + statusMask = (statusMask & ~(uint32_t)I2C_STAT_MSTSTATE_MASK) | (uint32_t)kI2C_MasterAddrNackFlag; + } + if ((statusMask & (uint32_t)I2C_STAT_SLVSTATE_MASK) == 0UL) + { + statusMask |= (uint32_t)kI2C_SlaveAddressedFlag; + } + if ((statusMask & (uint32_t)I2C_STAT_SLVIDX_MASK) == 0UL) + { + statusMask |= (uint32_t)kI2C_SlaveAddress0MatchFlag; + } + if (((statusMask & (uint32_t)I2C_STAT_SLVIDX_MASK) >> I2C_STAT_SLVIDX_SHIFT) == 3UL) + { + statusMask = (statusMask & ~(uint32_t)I2C_STAT_SLVIDX_MASK) | (uint32_t)kI2C_SlaveAddress3MatchFlag; + } + return statusMask; +} + +/*! + * brief Sets the I2C bus frequency for master transactions. + * + * The I2C master is automatically disabled and re-enabled as necessary to configure the baud + * rate. Do not call this function during a transfer, or the transfer is aborted. + * + * param base The I2C peripheral base address. + * param srcClock_Hz I2C functional clock frequency in Hertz. + * param baudRate_Bps Requested bus frequency in bits per second. + */ +void I2C_MasterSetBaudRate(I2C_Type *base, uint32_t baudRate_Bps, uint32_t srcClock_Hz) +{ + uint32_t scl, divider; + uint32_t mindivider; + uint32_t err, best_err; + uint32_t best_scl = 0U; + uint32_t best_div = 0U; + +#if defined(FSL_FEATURE_I2C_PREPCLKFRG_8MHZ) && (FSL_FEATURE_I2C_PREPCLKFRG_8MHZ) + /* + * RFT1717/RFT1437: workaround for hardware bug when using DMA + * I2C peripheral clock frequency has to be fixed at 8MHz + * source clock is 32MHz or 48MHz so divider is a round integer value + */ + best_div = srcClock_Hz / 8000000U; + best_scl = 8000000U / baudRate_Bps; + + if ((8000000U / best_scl - baudRate_Bps) > (baudRate_Bps - (8000000U / (best_scl + 1U)))) + { + best_scl = best_scl + 1U; + } + + /* + * Fallback to usual baudrate computation method, when: + * 1.Master SCL frequency does not fit in workaround range, + * 2.User's setting of baudRate_Bps is 400kHz while the clock frequency after divval is larger than 2MHz + */ + if ((best_scl > 18U) || ((best_scl < 4U)) || ((baudRate_Bps == 400000U) && (srcClock_Hz / best_div > 2000000U))) + { +#endif /*FSL_FEATURE_I2C_PREPCLKFRG_8MHZ*/ + + /* Calculate the minimal divider value to make sure the clock frequency after divval is not larger than 2MHz */ + /* This is required in RM in order to generate 400kHz baudrate */ + mindivider = ((srcClock_Hz * 10U) / 2000000U + 5U) / 10U; + /* If the scl value with current mindivider is smaller than 4, which is the minimal value register can achieve, + update mindivider */ + if ((srcClock_Hz / mindivider / baudRate_Bps) < 4U) + { + mindivider = srcClock_Hz / 4U / baudRate_Bps; + } + /* Calculate the ideal div and scl value*/ + best_err = 0U; + for (divider = mindivider; divider <= 0x10000U; divider++) + { + /* Calculte ideal scl value, round up the value */ + scl = ((srcClock_Hz * 10U) / (divider * baudRate_Bps) + 5U) / 10U; + + /* adjust it if it is out of range */ + scl = (scl > 18U) ? 18U : scl; + + /* calculate error */ + err = srcClock_Hz - (baudRate_Bps * scl * divider); + if ((err < best_err) || (best_err == 0U)) + { + best_div = divider; + best_scl = scl; + best_err = err; + } + + if ((err == 0U) || (scl <= 4U)) + { + /* either exact value was found + or scl is at its min (it would be even smaller in the next iteration for sure) */ + break; + } + } +#if defined(FSL_FEATURE_I2C_PREPCLKFRG_8MHZ) && (FSL_FEATURE_I2C_PREPCLKFRG_8MHZ) + } +#endif /*FSL_FEATURE_I2C_PREPCLKFRG_8MHZ*/ + base->CLKDIV = I2C_CLKDIV_DIVVAL(best_div - 1U); + if (best_scl % 2U == 0U) + { + base->MSTTIME = I2C_MSTTIME_MSTSCLLOW(best_scl / 2U - 2U) | I2C_MSTTIME_MSTSCLHIGH(best_scl / 2U - 2U); + } + else + { + base->MSTTIME = I2C_MSTTIME_MSTSCLLOW(best_scl / 2U - 1U) | I2C_MSTTIME_MSTSCLHIGH(best_scl / 2U - 2U); + } +} + +/*! + * brief Sets the I2C bus timeout value. + * + * If the SCL signal remains low or bus does not have event longer than the timeout value, kI2C_SclTimeoutFlag or + * kI2C_EventTimeoutFlag is set. This can indicete the bus is held by slave or any fault occurs to the I2C module. + * + * param base The I2C peripheral base address. + * param timeout_Ms Timeout value in millisecond. + * param srcClock_Hz I2C functional clock frequency in Hertz. + */ +void I2C_MasterSetTimeoutValue(I2C_Type *base, uint8_t timeout_Ms, uint32_t srcClock_Hz) +{ + assert((timeout_Ms != 0U) && (srcClock_Hz != 0U)); + + /* The low 4 bits of the timout reister TIMEOUT is hard-wired to be 1, so the the time out value is always 16 times + the I2C functional clock, we only need to calculate the high bits. */ + uint32_t timeoutValue = ((uint32_t)timeout_Ms * srcClock_Hz / 16UL / 100UL + 5UL) / 10UL; + if (timeoutValue > 0x1000UL) + { + timeoutValue = 0x1000UL; + } + timeoutValue = ((timeoutValue - 1UL) << 4UL) | 0xFUL; + base->TIMEOUT = timeoutValue; +} + +static status_t I2C_PendingStatusWait(I2C_Type *base) +{ + status_t result = kStatus_Success; + uint32_t status; + +#if I2C_RETRY_TIMES != 0U + uint32_t waitTimes = I2C_RETRY_TIMES; +#endif + + do + { + status = I2C_GetStatusFlags(base); + if ((status & (uint32_t)kI2C_EventTimeoutFlag) != 0U) + { + result = kStatus_I2C_EventTimeout; + } + if ((status & (uint32_t)kI2C_SclTimeoutFlag) != 0U) + { + result = kStatus_I2C_SclLowTimeout; + } +#if defined(FSL_FEATURE_I2C_TIMEOUT_RECOVERY) && FSL_FEATURE_I2C_TIMEOUT_RECOVERY + if (result != kStatus_Success) + { + I2C_MasterEnable(base, false); + I2C_MasterEnable(base, true); + break; + } +#endif +#if I2C_RETRY_TIMES != 0U + waitTimes--; + } while (((status & (uint32_t)kI2C_MasterPendingFlag) == 0U) && (waitTimes != 0U)); + + if (waitTimes == 0U) + { +#if defined(FSL_FEATURE_I2C_TIMEOUT_RECOVERY) && FSL_FEATURE_I2C_TIMEOUT_RECOVERY + I2C_MasterEnable(base, false); + I2C_MasterEnable(base, true); +#endif + return kStatus_I2C_Timeout; + } +#else + } while ((status & (uint32_t)kI2C_MasterPendingFlag) == 0U); +#endif + + if ((status & (uint32_t)kI2C_MasterArbitrationLostFlag) != 0U) + { + result = kStatus_I2C_ArbitrationLost; + } + + if ((status & (uint32_t)kI2C_MasterStartStopErrorFlag) != 0U) + { + result = kStatus_I2C_StartStopError; + } + + /* Clear controller state. */ + I2C_ClearStatusFlags( + base, (uint32_t)kI2C_MasterAllClearFlags | (uint32_t)kI2C_EventTimeoutFlag | (uint32_t)kI2C_SclTimeoutFlag); + + return result; +} + +/*! + * brief Sends a START on the I2C bus. + * + * This function is used to initiate a new master mode transfer by sending the START signal. + * The slave address is sent following the I2C START signal. + * + * param base I2C peripheral base pointer + * param address 7-bit slave device address. + * param direction Master transfer directions(transmit/receive). + * retval kStatus_Success Successfully send the start signal. + * retval kStatus_I2C_Busy Current bus is busy. + */ +status_t I2C_MasterStart(I2C_Type *base, uint8_t address, i2c_direction_t direction) +{ + status_t result; + result = I2C_PendingStatusWait(base); + if (result != kStatus_Success) + { + return result; + } + + /* Write Address and RW bit to data register */ + base->MSTDAT = ((uint32_t)address << 1) | ((uint32_t)direction & 1U); + /* Start the transfer */ + base->MSTCTL = I2C_MSTCTL_MSTSTART_MASK; + + return kStatus_Success; +} + +/*! + * brief Sends a STOP signal on the I2C bus. + * + * retval kStatus_Success Successfully send the stop signal. + * retval kStatus_I2C_Timeout Send stop signal failed, timeout. + */ +status_t I2C_MasterStop(I2C_Type *base) +{ + status_t result = I2C_PendingStatusWait(base); + if (result != kStatus_Success) + { + return result; + } + + base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK; + return kStatus_Success; +} + +/*! + * brief Performs a polling send transfer on the I2C bus. + * + * Sends up to a txSize number of bytes to the previously addressed slave device. The slave may + * reply with a NAK to any byte in order to terminate the transfer early. If this happens, this + * function returns #kStatus_I2C_Nak. + * + * param base The I2C peripheral base address. + * param txBuff The pointer to the data to be transferred. + * param txSize The length in bytes of the data to be transferred. + * param flags Transfer control flag to control special behavior like suppressing start or stop, for normal transfers + * use kI2C_TransferDefaultFlag + * retval kStatus_Success Data was sent successfully. + * retval #kStatus_I2C_Busy Another master is currently utilizing the bus. + * retval #kStatus_I2C_Nak The slave device sent a NAK in response to a byte. + * retval #kStatus_I2C_ArbitrationLost Arbitration lost error. + */ +status_t I2C_MasterWriteBlocking(I2C_Type *base, const void *txBuff, size_t txSize, uint32_t flags) +{ + uint32_t master_state; + status_t err; + + const uint8_t *buf = (const uint8_t *)txBuff; + + assert(txBuff != NULL); + + err = kStatus_Success; + while (txSize != 0U) + { + err = I2C_PendingStatusWait(base); + + if (err != kStatus_Success) + { + return err; + } + + master_state = (base->STAT & I2C_STAT_MSTSTATE_MASK) >> I2C_STAT_MSTSTATE_SHIFT; + switch (master_state) + { + case I2C_STAT_MSTCODE_TXREADY: + /* ready to send next byte */ + base->MSTDAT = *buf++; + txSize--; + base->MSTCTL = I2C_MSTCTL_MSTCONTINUE_MASK; + break; + + case I2C_STAT_MSTCODE_NACKADR: + case I2C_STAT_MSTCODE_NACKDAT: + err = kStatus_I2C_Nak; + /* Issue nack signal when nacked by slave. */ + (void)I2C_MasterStop(base); + break; + + default: + /* unexpected state */ + err = kStatus_I2C_UnexpectedState; + break; + } + + if (err != kStatus_Success) + { + return err; + } + } + + err = I2C_PendingStatusWait(base); + + if (err != kStatus_Success) + { + return err; + } + +#if !I2C_MASTER_TRANSMIT_IGNORE_LAST_NACK + /* Check nack signal. If master is nacked by slave of the last byte, return kStatus_I2C_Nak. */ + if (((base->STAT & I2C_STAT_MSTSTATE_MASK) >> I2C_STAT_MSTSTATE_SHIFT) == (uint32_t)I2C_STAT_MSTCODE_NACKDAT) + { + (void)I2C_MasterStop(base); + return kStatus_I2C_Nak; + } +#endif + + if (0U == (flags & (uint32_t)kI2C_TransferNoStopFlag)) + { + /* Initiate stop */ + base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK; + err = I2C_PendingStatusWait(base); + if (err != kStatus_Success) + { + return err; + } + } + + return kStatus_Success; +} + +/*! + * brief Performs a polling receive transfer on the I2C bus. + * + * param base The I2C peripheral base address. + * param rxBuff The pointer to the data to be transferred. + * param rxSize The length in bytes of the data to be transferred. + * param flags Transfer control flag to control special behavior like suppressing start or stop, for normal transfers + * use kI2C_TransferDefaultFlag + * retval kStatus_Success Data was received successfully. + * retval #kStatus_I2C_Busy Another master is currently utilizing the bus. + * retval #kStatus_I2C_Nak The slave device sent a NAK in response to a byte. + * retval #kStatus_I2C_ArbitrationLost Arbitration lost error. + */ +status_t I2C_MasterReadBlocking(I2C_Type *base, void *rxBuff, size_t rxSize, uint32_t flags) +{ + uint32_t master_state; + status_t err; + + uint8_t *buf = (uint8_t *)(rxBuff); + + assert(rxBuff != NULL); + + err = kStatus_Success; + while (rxSize != 0U) + { + err = I2C_PendingStatusWait(base); + + if (err != kStatus_Success) + { + return err; + } + + master_state = (base->STAT & I2C_STAT_MSTSTATE_MASK) >> I2C_STAT_MSTSTATE_SHIFT; + switch (master_state) + { + case I2C_STAT_MSTCODE_RXREADY: + /* ready to send next byte */ + *(buf++) = (uint8_t)base->MSTDAT; + if (--rxSize != 0U) + { + base->MSTCTL = I2C_MSTCTL_MSTCONTINUE_MASK; + } + else + { + if ((flags & (uint32_t)kI2C_TransferNoStopFlag) == 0U) + { + /* initiate NAK and stop */ + base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK; + err = I2C_PendingStatusWait(base); + } + } + break; + + case I2C_STAT_MSTCODE_NACKADR: + case I2C_STAT_MSTCODE_NACKDAT: + /* slave nacked the last byte */ + err = kStatus_I2C_Nak; + break; + + default: + /* unexpected state */ + err = kStatus_I2C_UnexpectedState; + break; + } + + if (err != kStatus_Success) + { + return err; + } + } + + return kStatus_Success; +} + +static status_t I2C_MasterCheckStartResponse(I2C_Type *base) +{ + /* Wait for start signal to be transmitted. */ + status_t result = I2C_PendingStatusWait(base); + + if (result != kStatus_Success) + { + return result; + } + + if (((base->STAT & I2C_STAT_MSTSTATE_MASK) >> I2C_STAT_MSTSTATE_SHIFT) == I2C_STAT_MSTCODE_NACKADR) + { + (void)I2C_MasterStop(base); + return kStatus_I2C_Addr_Nak; + } + return kStatus_Success; +} + +/*! + * brief Performs a master polling transfer on the I2C bus. + * + * note The API does not return until the transfer succeeds or fails due + * to arbitration lost or receiving a NAK. + * + * param base I2C peripheral base address. + * param xfer Pointer to the transfer structure. + * retval kStatus_Success Successfully complete the data transmission. + * retval kStatus_I2C_Busy Previous transmission still not finished. + * retval kStatus_I2C_Timeout Transfer error, wait signal timeout. + * retval kStatus_I2C_ArbitrationLost Transfer error, arbitration lost. + * retval kStataus_I2C_Nak Transfer error, receive NAK during transfer. + * retval kStataus_I2C_Addr_Nak Transfer error, receive NAK during addressing. + */ +status_t I2C_MasterTransferBlocking(I2C_Type *base, i2c_master_transfer_t *xfer) +{ + status_t result = kStatus_Success; + uint32_t subaddress; + uint8_t subaddrBuf[4]; + i2c_direction_t direction; + int i; + + assert(xfer != NULL); + + /* If start signal is requested, send start signal. */ + if (0U == (xfer->flags & (uint32_t)kI2C_TransferNoStartFlag)) + { + direction = (xfer->subaddressSize != 0U) ? kI2C_Write : xfer->direction; + result = I2C_MasterStart(base, xfer->slaveAddress, direction); + if (result == kStatus_Success) + { + result = I2C_MasterCheckStartResponse(base); + if (result != kStatus_Success) + { + return result; + } + if ((xfer->subaddressSize) != 0U) + { + /* Prepare subaddress transmit buffer, most significant byte is stored at the lowest address */ + subaddress = xfer->subaddress; + for (i = (int)xfer->subaddressSize - 1; i >= 0; i--) + { + subaddrBuf[i] = (uint8_t)subaddress & 0xffU; + subaddress >>= 8; + } + /* Send subaddress. */ + result = + I2C_MasterWriteBlocking(base, subaddrBuf, xfer->subaddressSize, (uint32_t)kI2C_TransferNoStopFlag); + if (result != kStatus_Success) + { + if (result == kStatus_I2C_Nak) + { + (void)I2C_MasterStop(base); + return kStatus_I2C_Addr_Nak; + } + } + else if (xfer->direction == kI2C_Read) + { + result = I2C_MasterRepeatedStart(base, xfer->slaveAddress, xfer->direction); + if (result == kStatus_Success) + { + result = I2C_MasterCheckStartResponse(base); + if (result != kStatus_Success) + { + return result; + } + } + } + else + { + /* Empty else block to avoid MISRA 14.1 violation. */ + } + } + } + } + + if (result == kStatus_Success) + { + if ((xfer->direction == kI2C_Write) && (xfer->dataSize > 0U)) + { + /* Transmit data. */ + result = I2C_MasterWriteBlocking(base, xfer->data, xfer->dataSize, xfer->flags); + } + else + { + if ((xfer->direction == kI2C_Read) && (xfer->dataSize > 0U)) + { + /* Receive Data. */ + result = I2C_MasterReadBlocking(base, xfer->data, xfer->dataSize, xfer->flags); + } + } + } + + if (result == kStatus_I2C_Nak) + { + (void)I2C_MasterStop(base); + } + + return result; +} + +/*! + * brief Creates a new handle for the I2C master non-blocking APIs. + * + * The creation of a handle is for use with the non-blocking APIs. Once a handle + * is created, there is not a corresponding destroy handle. If the user wants to + * terminate a transfer, the I2C_MasterTransferAbort() API shall be called. + * + * param base The I2C peripheral base address. + * param[out] handle Pointer to the I2C master driver handle. + * param callback User provided pointer to the asynchronous callback function. + * param userData User provided pointer to the application callback data. + */ +void I2C_MasterTransferCreateHandle(I2C_Type *base, + i2c_master_handle_t *handle, + i2c_master_transfer_callback_t callback, + void *userData) +{ + assert(handle != NULL); + + uint32_t instance; + i2c_to_flexcomm_t handler; + handler.i2c_master_handler = I2C_MasterTransferHandleIRQ; + + /* Clear out the handle. */ + (void)memset(handle, 0, sizeof(*handle)); + + /* Look up instance number */ + instance = I2C_GetInstance(base); + + /* Save base and instance. */ + handle->completionCallback = callback; + handle->userData = userData; + + FLEXCOMM_SetIRQHandler(base, handler.flexcomm_handler, handle); + + /* Clear internal IRQ enables and enable NVIC IRQ. */ + I2C_DisableInterrupts(base, (uint32_t)kI2C_MasterIrqFlags); + (void)EnableIRQ(s_i2cIRQ[instance]); +} + +/*! + * brief Performs a non-blocking transaction on the I2C bus. + * + * param base The I2C peripheral base address. + * param handle Pointer to the I2C master driver handle. + * param xfer The pointer to the transfer descriptor. + * retval kStatus_Success The transaction was started successfully. + * retval #kStatus_I2C_Busy Either another master is currently utilizing the bus, or a non-blocking + * transaction is already in progress. + */ +status_t I2C_MasterTransferNonBlocking(I2C_Type *base, i2c_master_handle_t *handle, i2c_master_transfer_t *xfer) +{ + status_t result; + + assert(handle != NULL); + assert(xfer != NULL); + assert(xfer->subaddressSize <= sizeof(xfer->subaddress)); + + /* Return busy if another transaction is in progress. */ + if (handle->state != (uint8_t)kIdleState) + { + return kStatus_I2C_Busy; + } + + /* Disable I2C IRQ sources while we configure stuff. */ + I2C_DisableInterrupts(base, (uint32_t)kI2C_MasterIrqFlags); + + /* Prepare transfer state machine. */ + result = I2C_InitTransferStateMachine(base, handle, xfer); + + /* Clear error flags. */ + I2C_ClearStatusFlags(base, I2C_STAT_MSTARBLOSS_MASK | I2C_STAT_MSTSTSTPERR_MASK); + + /* Enable I2C internal IRQ sources. */ + I2C_EnableInterrupts(base, (uint32_t)kI2C_MasterIrqFlags); + + return result; +} + +/*! + * brief Returns number of bytes transferred so far. + * param base The I2C peripheral base address. + * param handle Pointer to the I2C master driver handle. + * param[out] count Number of bytes transferred so far by the non-blocking transaction. + * retval kStatus_Success + * retval #kStatus_I2C_Busy + */ +status_t I2C_MasterTransferGetCount(I2C_Type *base, i2c_master_handle_t *handle, size_t *count) +{ + assert(handle != NULL); + + if (NULL == count) + { + return kStatus_InvalidArgument; + } + + /* Catch when there is not an active transfer. */ + if (handle->state == (uint8_t)kIdleState) + { + *count = 0; + return kStatus_NoTransferInProgress; + } + + /* There is no necessity to disable interrupts as we read a single integer value */ + *count = handle->transferCount; + return kStatus_Success; +} + +/*! + * brief Terminates a non-blocking I2C master transmission early. + * + * note It is not safe to call this function from an IRQ handler that has a higher priority than the + * I2C peripheral's IRQ priority. + * + * param base The I2C peripheral base address. + * param handle Pointer to the I2C master driver handle. + * retval kStatus_Success A transaction was successfully aborted. + * retval #kStatus_I2C_Timeout Timeout during polling for flags. + */ +status_t I2C_MasterTransferAbort(I2C_Type *base, i2c_master_handle_t *handle) +{ + status_t result = kStatus_Success; + uint32_t master_state; + + if (handle->state != (uint8_t)kIdleState) + { + /* Disable internal IRQ enables. */ + I2C_DisableInterrupts(base, (uint32_t)kI2C_MasterIrqFlags); + + /* Wait until module is ready */ + result = I2C_PendingStatusWait(base); + + if (result != kStatus_Success) + { + handle->state = (uint8_t)kIdleState; + return result; + } + + /* Get the state of the I2C module */ + master_state = (base->STAT & I2C_STAT_MSTSTATE_MASK) >> I2C_STAT_MSTSTATE_SHIFT; + + if (master_state != (uint32_t)I2C_STAT_MSTCODE_IDLE) + { + /* Send a stop command to finalize the transfer. */ + base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK; + + /* Wait until the STOP is completed */ + result = I2C_PendingStatusWait(base); + + if (result != kStatus_Success) + { + handle->state = (uint8_t)kIdleState; + return result; + } + } + + /* Reset handle. */ + handle->state = (uint8_t)kIdleState; + handle->checkAddrNack = false; + } + return kStatus_Success; +} + +static status_t I2C_InitTransferStateMachine(I2C_Type *base, i2c_master_handle_t *handle, i2c_master_transfer_t *xfer) +{ + struct _i2c_master_transfer *transfer; + + handle->transfer = *xfer; + transfer = &(handle->transfer); + + handle->transferCount = 0; + handle->remainingBytes = transfer->dataSize; + handle->buf = (uint8_t *)transfer->data; + handle->remainingSubaddr = 0; + handle->checkAddrNack = false; + + if ((transfer->flags & (uint32_t)kI2C_TransferNoStartFlag) != 0U) + { + /* Start condition shall be ommited, switch directly to next phase */ + if (transfer->dataSize == 0U) + { + handle->state = (uint8_t)kStopState; + } + else if (handle->transfer.direction == kI2C_Write) + { + handle->state = (uint8_t)kTransmitDataState; + } + else if (handle->transfer.direction == kI2C_Read) + { + handle->state = (uint8_t)kReceiveDataBeginState; + } + else + { + return kStatus_I2C_InvalidParameter; + } + } + else + { + if (transfer->subaddressSize != 0U) + { + int i; + uint32_t subaddress; + + if (transfer->subaddressSize > sizeof(handle->subaddrBuf)) + { + return kStatus_I2C_InvalidParameter; + } + + /* Prepare subaddress transmit buffer, most significant byte is stored at the lowest address */ + subaddress = xfer->subaddress; + for (i = (int)xfer->subaddressSize - 1; i >= 0; i--) + { + handle->subaddrBuf[i] = (uint8_t)subaddress & 0xffU; + subaddress >>= 8; + } + handle->remainingSubaddr = transfer->subaddressSize; + } + handle->state = (uint8_t)kStartState; + handle->checkAddrNack = true; + } + + return kStatus_Success; +} + +static status_t I2C_RunTransferStateMachine(I2C_Type *base, i2c_master_handle_t *handle, bool *isDone) +{ + uint32_t status; + uint32_t master_state; + struct _i2c_master_transfer *transfer; + status_t err; + + transfer = &(handle->transfer); + bool ignoreNak = ((handle->state == (uint8_t)kWaitForCompletionState) && (handle->remainingBytes == 0U)) +#if I2C_MASTER_TRANSMIT_IGNORE_LAST_NACK + /* If master is nacked by slave after the last byte during transmit, ignore the nack. */ + || ((handle->state == (uint8_t)kStopState) && (handle->remainingBytes == 0U)) +#endif + ; + + *isDone = false; + + status = I2C_GetStatusFlags(base); + + if ((status & I2C_STAT_MSTARBLOSS_MASK) != 0U) + { + I2C_ClearStatusFlags(base, I2C_STAT_MSTARBLOSS_MASK); + return kStatus_I2C_ArbitrationLost; + } + + if ((status & I2C_STAT_MSTSTSTPERR_MASK) != 0U) + { + I2C_ClearStatusFlags(base, I2C_STAT_MSTSTSTPERR_MASK); + return kStatus_I2C_StartStopError; + } + + /* Event timeout happens when the time since last bus event has been longer than the time specified by TIMEOUT + register. eg: Start signal fails to generate, no error status is set and transfer hangs if glitch on bus happens + before, the timeout status can be used to avoid the transfer hangs indefinitely. */ + if ((status & (uint32_t)kI2C_EventTimeoutFlag) != 0U) + { + I2C_ClearStatusFlags(base, (uint32_t)kI2C_EventTimeoutFlag); + return kStatus_I2C_EventTimeout; + } + + /* SCL timeout happens when the slave is holding the SCL line low and the time has been longer than the time + specified by TIMEOUT register. */ + if ((status & (uint32_t)kI2C_SclTimeoutFlag) != 0U) + { + I2C_ClearStatusFlags(base, (uint32_t)kI2C_SclTimeoutFlag); + return kStatus_I2C_SclLowTimeout; + } + + if ((status & I2C_STAT_MSTPENDING_MASK) == 0U) + { + return kStatus_I2C_Busy; + } + + /* Get the hardware state of the I2C module */ + master_state = (base->STAT & I2C_STAT_MSTSTATE_MASK) >> I2C_STAT_MSTSTATE_SHIFT; + if (((master_state == (uint32_t)I2C_STAT_MSTCODE_NACKADR) || + (master_state == (uint32_t)I2C_STAT_MSTCODE_NACKDAT)) && + (ignoreNak != true)) + { + /* Slave NACKed last byte, issue stop and return error */ + base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK; + handle->state = (uint8_t)kWaitForCompletionState; + /* If master is nacked during slave probe or during sending subaddress, return kStatus_I2C_ADDR_Nak. */ + if ((master_state == (uint32_t)I2C_STAT_MSTCODE_NACKADR) || (handle->checkAddrNack)) + { + return kStatus_I2C_Addr_Nak; + } + else /* Otherwise just return kStatus_I2C_Nak */ + { + return kStatus_I2C_Nak; + } + } + + err = kStatus_Success; + switch (handle->state) + { + case (uint8_t)kStartState: + if (handle->remainingSubaddr != 0U) + { + /* Subaddress takes precedence over the data transfer, direction is always "write" in this case */ + base->MSTDAT = (uint32_t)transfer->slaveAddress << 1U; + handle->state = (uint8_t)kTransmitSubaddrState; + } + else if (transfer->direction == kI2C_Write) + { + base->MSTDAT = (uint32_t)transfer->slaveAddress << 1; + handle->state = (handle->remainingBytes != 0U) ? (uint8_t)kTransmitDataState : (uint8_t)kStopState; + } + else + { + base->MSTDAT = ((uint32_t)transfer->slaveAddress << 1) | 1u; + handle->state = (handle->remainingBytes != 0U) ? (uint8_t)kReceiveDataState : (uint8_t)kStopState; + } + /* Send start condition */ + base->MSTCTL = I2C_MSTCTL_MSTSTART_MASK; + break; + + case (uint8_t)kTransmitSubaddrState: + if (master_state != (uint32_t)I2C_STAT_MSTCODE_TXREADY) + { + return kStatus_I2C_UnexpectedState; + } + /* Most significant subaddress byte comes first */ + base->MSTDAT = handle->subaddrBuf[handle->transfer.subaddressSize - handle->remainingSubaddr]; + base->MSTCTL = I2C_MSTCTL_MSTCONTINUE_MASK; + if (--(handle->remainingSubaddr) != 0U) + { + /* There are still subaddress bytes to be transmitted */ + break; + } + if (handle->remainingBytes != 0U) + { + /* There is data to be transferred, if there is write to read turnaround it is necessary to perform + * repeated start */ + handle->state = (transfer->direction == kI2C_Read) ? (uint8_t)kStartState : (uint8_t)kTransmitDataState; + } + else + { + /* No more data, schedule stop condition */ + handle->state = (uint8_t)kStopState; + } + break; + + case (uint8_t)kTransmitDataState: + handle->checkAddrNack = false; + if (master_state != (uint32_t)I2C_STAT_MSTCODE_TXREADY) + { + return kStatus_I2C_UnexpectedState; + } + base->MSTDAT = *(handle->buf)++; + base->MSTCTL = I2C_MSTCTL_MSTCONTINUE_MASK; + if (--handle->remainingBytes == 0U) + { + /* No more data, schedule stop condition */ + handle->state = (uint8_t)kStopState; + } + handle->transferCount++; + break; + + case (uint8_t)kReceiveDataBeginState: + handle->checkAddrNack = false; + if (master_state != (uint32_t)I2C_STAT_MSTCODE_RXREADY) + { + return kStatus_I2C_UnexpectedState; + } + (void)base->MSTDAT; + base->MSTCTL = I2C_MSTCTL_MSTCONTINUE_MASK; + handle->state = (uint8_t)kReceiveDataState; + break; + + case (uint8_t)kReceiveDataState: + handle->checkAddrNack = false; + if (master_state != (uint32_t)I2C_STAT_MSTCODE_RXREADY) + { + return kStatus_I2C_UnexpectedState; + } + *(handle->buf)++ = (uint8_t)base->MSTDAT; + if (--handle->remainingBytes != 0U) + { + base->MSTCTL = I2C_MSTCTL_MSTCONTINUE_MASK; + } + else + { + /* No more data expected, issue NACK and STOP right away */ + if (0U == (transfer->flags & (uint32_t)kI2C_TransferNoStopFlag)) + { + base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK; + } + handle->state = (uint8_t)kWaitForCompletionState; + } + handle->transferCount++; + break; + + case (uint8_t)kStopState: + handle->checkAddrNack = false; + if ((transfer->flags & (uint32_t)kI2C_TransferNoStopFlag) != 0U) + { + /* Stop condition is omitted, we are done */ + *isDone = true; + handle->state = (uint8_t)kIdleState; + break; + } + /* Send stop condition */ + base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK; + handle->state = (uint8_t)kWaitForCompletionState; + break; + + case (uint8_t)kWaitForCompletionState: + *isDone = true; + handle->state = (uint8_t)kIdleState; + break; + + case (uint8_t)kIdleState: + default: + /* State machine shall not be invoked again once it enters the idle state */ + err = kStatus_I2C_UnexpectedState; + break; + } + + return err; +} + +/*! + * brief Reusable routine to handle master interrupts. + * note This function does not need to be called unless you are reimplementing the + * nonblocking API's interrupt handler routines to add special functionality. + * param base The I2C peripheral base address. + * param handle Pointer to the I2C master driver handle. + */ +void I2C_MasterTransferHandleIRQ(I2C_Type *base, i2c_master_handle_t *handle) +{ + bool isDone; + status_t result; + + /* Don't do anything if we don't have a valid handle. */ + if (NULL == handle) + { + return; + } + + result = I2C_RunTransferStateMachine(base, handle, &isDone); + + if ((result != kStatus_Success) || isDone) + { + /* Restore handle to idle state. */ + handle->state = (uint8_t)kIdleState; + + /* Disable internal IRQ enables. */ + I2C_DisableInterrupts(base, (uint32_t)kI2C_MasterIrqFlags); + + /* Invoke callback. */ + if (handle->completionCallback != NULL) + { + handle->completionCallback(base, handle, result, handle->userData); + } + } +} + +static void I2C_SlaveInternalStateMachineReset(I2C_Type *base) +{ + I2C_SlaveEnable(base, false); /* clear SLVEN Slave enable bit */ +} + +static status_t I2C_SlaveDivVal(uint32_t srcClock_Hz, i2c_slave_bus_speed_t busSpeed, uint32_t *divVal) +{ + uint32_t dataSetupTime_ns; + + switch ((uint8_t)(busSpeed)) + { + case (uint8_t)kI2C_SlaveStandardMode: + dataSetupTime_ns = 250U; + break; + + case (uint8_t)kI2C_SlaveFastMode: + dataSetupTime_ns = 100U; + break; + + case (uint8_t)kI2C_SlaveFastModePlus: + dataSetupTime_ns = 50U; + break; + + case (uint8_t)kI2C_SlaveHsMode: + dataSetupTime_ns = 10U; + break; + + default: + dataSetupTime_ns = 0U; + break; + } + + if (0U == dataSetupTime_ns) + { + return kStatus_InvalidArgument; + } + + /* divVal = (sourceClock_Hz / 1000000) * (dataSetupTime_ns / 1000) */ + *divVal = srcClock_Hz / 1000U; + *divVal = (*divVal) * dataSetupTime_ns; + *divVal = (*divVal) / 1000000U; + + if ((*divVal) > I2C_CLKDIV_DIVVAL_MASK) + { + *divVal = I2C_CLKDIV_DIVVAL_MASK; + } + + return kStatus_Success; +} + +static uint32_t I2C_SlavePollPending(I2C_Type *base) +{ + uint32_t stat; + +#if I2C_RETRY_TIMES != 0U + uint32_t waitTimes = I2C_RETRY_TIMES; +#endif + do + { + stat = base->STAT; +#if I2C_RETRY_TIMES != 0U + waitTimes--; + } while ((0U == (stat & I2C_STAT_SLVPENDING_MASK)) && (waitTimes != 0U)); + + if (waitTimes == 0U) + { + return (uint32_t)kStatus_I2C_Timeout; + } +#else + } while (0U == (stat & I2C_STAT_SLVPENDING_MASK)); +#endif + + return stat; +} + +static void I2C_SlaveInvokeEvent(I2C_Type *base, i2c_slave_handle_t *handle, i2c_slave_transfer_event_t event) +{ + uint32_t eventMask = handle->transfer.eventMask; + handle->transfer.event = event; + if (((handle->callback) != NULL) && ((eventMask & (uint32_t)event) != 0U)) + { + handle->callback(base, &handle->transfer, handle->userData); + + size_t txSize = handle->transfer.txSize; + size_t rxSize = handle->transfer.rxSize; + /* if after event callback we have data buffer (callback func has added new data), keep transfer busy */ + if (false == handle->isBusy) + { + if (((handle->transfer.txData != NULL) && (txSize != 0U)) || + ((handle->transfer.rxData != NULL) && (rxSize != 0U))) + { + handle->isBusy = true; + } + } + + /* Clear the transferred count now that we have a new buffer. */ + if ((event == kI2C_SlaveReceiveEvent) || (event == kI2C_SlaveTransmitEvent)) + { + handle->transfer.transferredCount = 0; + } + } +} + +static bool I2C_SlaveAddressIRQ(I2C_Type *base, i2c_slave_handle_t *handle) +{ + uint8_t addressByte0; + size_t txSize; + size_t rxSize; + + addressByte0 = (uint8_t)base->SLVDAT; + + /* store the matched address */ + handle->transfer.receivedAddress = addressByte0; + + /* R/nW */ + if ((addressByte0 & 1U) != 0U) + { + txSize = handle->transfer.txSize; + /* if we have no data in this transfer, call callback to get new */ + if ((handle->transfer.txData == NULL) || (txSize == 0U)) + { + I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveTransmitEvent); + } + + txSize = handle->transfer.txSize; + /* NACK if we have no data in this transfer. */ + if ((handle->transfer.txData == NULL) || (txSize == 0U)) + { + base->SLVCTL = I2C_SLVCTL_SLVNACK_MASK; + return false; + } + + /* master wants to read, so slave transmit is next state */ + handle->slaveFsm = kI2C_SlaveFsmTransmit; + } + else + { + rxSize = handle->transfer.rxSize; + /* if we have no receive buffer in this transfer, call callback to get new */ + if ((handle->transfer.rxData == NULL) || (rxSize == 0U)) + { + I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveReceiveEvent); + } + + rxSize = handle->transfer.rxSize; + /* NACK if we have no data in this transfer */ + if ((handle->transfer.rxData == NULL) || (rxSize == 0U)) + { + base->SLVCTL = I2C_SLVCTL_SLVNACK_MASK; + return false; + } + + /* master wants write, so slave receive is next state */ + handle->slaveFsm = kI2C_SlaveFsmReceive; + } + + /* continue transaction */ + base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK; + + return true; +} + +static status_t I2C_SlaveTransferNonBlockingInternal(I2C_Type *base, + i2c_slave_handle_t *handle, + const void *txData, + size_t txSize, + void *rxData, + size_t rxSize, + uint32_t eventMask) +{ + assert(handle != NULL); + + status_t status; + status = kStatus_Success; + + /* Disable I2C IRQ sources while we configure stuff. */ + I2C_DisableInterrupts(base, (uint32_t)kI2C_SlaveIrqFlags); + + /* Return busy if another transaction is in progress. */ + if (handle->isBusy) + { + status = kStatus_I2C_Busy; + } + + /* Save transfer into handle. */ + handle->transfer.txData = (const uint8_t *)txData; + handle->transfer.txSize = txSize; + handle->transfer.rxData = (uint8_t *)rxData; + handle->transfer.rxSize = rxSize; + handle->transfer.transferredCount = 0; + handle->transfer.eventMask = eventMask | (uint32_t)kI2C_SlaveTransmitEvent | (uint32_t)kI2C_SlaveReceiveEvent; + handle->isBusy = true; + + /* Set the SLVEN bit to 1 in the CFG register. */ + I2C_SlaveEnable(base, true); + + /* Clear w1c flags. */ + base->STAT |= 0u; + + /* Enable I2C internal IRQ sources. */ + I2C_EnableInterrupts(base, (uint32_t)kI2C_SlaveIrqFlags); + + return status; +} + +/*! + * brief Starts accepting master read from slave requests. + * + * The function can be called in response to #kI2C_SlaveTransmitEvent callback to start a new slave Tx transfer + * from within the transfer callback. + * + * The set of events received by the callback is customizable. To do so, set the a eventMask parameter to + * the OR'd combination of #i2c_slave_transfer_event_t enumerators for the events you wish to receive. + * The #kI2C_SlaveTransmitEvent and #kI2C_SlaveReceiveEvent events are always enabled and do not need + * to be included in the mask. Alternatively, you can pass 0 to get a default set of only the transmit and + * receive events that are always enabled. In addition, the #kI2C_SlaveAllEvents constant is provided as + * a convenient way to enable all events. + * + * param base The I2C peripheral base address. + * param transfer Pointer to #i2c_slave_transfer_t structure. + * param txData Pointer to data to send to master. + * param txSize Size of txData in bytes. + * param eventMask Bit mask formed by OR'ing together #i2c_slave_transfer_event_t enumerators to specify + * which events to send to the callback. Other accepted values are 0 to get a default set of + * only the transmit and receive events, and #kI2C_SlaveAllEvents to enable all events. + * + * retval kStatus_Success Slave transfers were successfully started. + * retval #kStatus_I2C_Busy Slave transfers have already been started on this handle. + */ +status_t I2C_SlaveSetSendBuffer( + I2C_Type *base, volatile i2c_slave_transfer_t *transfer, const void *txData, size_t txSize, uint32_t eventMask) +{ + return I2C_SlaveTransferNonBlockingInternal(base, transfer->handle, txData, txSize, NULL, 0u, eventMask); +} + +/*! + * brief Starts accepting master write to slave requests. + * + * The function can be called in response to #kI2C_SlaveReceiveEvent callback to start a new slave Rx transfer + * from within the transfer callback. + * + * The set of events received by the callback is customizable. To do so, set the a eventMask parameter to + * the OR'd combination of #i2c_slave_transfer_event_t enumerators for the events you wish to receive. + * The #kI2C_SlaveTransmitEvent and #kI2C_SlaveReceiveEvent events are always enabled and do not need + * to be included in the mask. Alternatively, you can pass 0 to get a default set of only the transmit and + * receive events that are always enabled. In addition, the #kI2C_SlaveAllEvents constant is provided as + * a convenient way to enable all events. + * + * param base The I2C peripheral base address. + * param transfer Pointer to #i2c_slave_transfer_t structure. + * param rxData Pointer to data to store data from master. + * param rxSize Size of rxData in bytes. + * param eventMask Bit mask formed by OR'ing together #i2c_slave_transfer_event_t enumerators to specify + * which events to send to the callback. Other accepted values are 0 to get a default set of + * only the transmit and receive events, and #kI2C_SlaveAllEvents to enable all events. + * + * retval kStatus_Success Slave transfers were successfully started. + * retval #kStatus_I2C_Busy Slave transfers have already been started on this handle. + */ +status_t I2C_SlaveSetReceiveBuffer( + I2C_Type *base, volatile i2c_slave_transfer_t *transfer, void *rxData, size_t rxSize, uint32_t eventMask) +{ + return I2C_SlaveTransferNonBlockingInternal(base, transfer->handle, NULL, 0u, rxData, rxSize, eventMask); +} + +/*! + * brief Configures Slave Address n register. + * + * This function writes new value to Slave Address register. + * + * param base The I2C peripheral base address. + * param addressRegister The module supports multiple address registers. The parameter determines which one shall be + * changed. + * param address The slave address to be stored to the address register for matching. + * param addressDisable Disable matching of the specified address register. + */ +void I2C_SlaveSetAddress(I2C_Type *base, + i2c_slave_address_register_t addressRegister, + uint8_t address, + bool addressDisable) +{ + base->SLVADR[addressRegister] = I2C_SLVADR_SLVADR(address) | I2C_SLVADR_SADISABLE(addressDisable); +} + +/*! + * brief Provides a default configuration for the I2C slave peripheral. + * + * This function provides the following default configuration for the I2C slave peripheral: + * code + * slaveConfig->enableSlave = true; + * slaveConfig->address0.disable = false; + * slaveConfig->address0.address = 0u; + * slaveConfig->address1.disable = true; + * slaveConfig->address2.disable = true; + * slaveConfig->address3.disable = true; + * slaveConfig->busSpeed = kI2C_SlaveStandardMode; + * endcode + * + * After calling this function, override any settings to customize the configuration, + * prior to initializing the master driver with I2C_SlaveInit(). Be sure to override at least the a + * address0.address member of the configuration structure with the desired slave address. + * + * param[out] slaveConfig User provided configuration structure that is set to default values. Refer to + * #i2c_slave_config_t. + */ +void I2C_SlaveGetDefaultConfig(i2c_slave_config_t *slaveConfig) +{ + assert(slaveConfig != NULL); + + i2c_slave_config_t mySlaveConfig = {0}; + + /* default config enables slave address 0 match to general I2C call address zero */ + mySlaveConfig.enableSlave = true; + mySlaveConfig.address1.addressDisable = true; + mySlaveConfig.address2.addressDisable = true; + mySlaveConfig.address3.addressDisable = true; + + *slaveConfig = mySlaveConfig; +} + +/*! + * brief Initializes the I2C slave peripheral. + * + * This function enables the peripheral clock and initializes the I2C slave peripheral as described by the user + * provided configuration. + * + * param base The I2C peripheral base address. + * param slaveConfig User provided peripheral configuration. Use I2C_SlaveGetDefaultConfig() to get a set of defaults + * that you can override. + * param srcClock_Hz Frequency in Hertz of the I2C functional clock. Used to calculate CLKDIV value to provide + * enough + * data setup time for master when slave stretches the clock. + */ +status_t I2C_SlaveInit(I2C_Type *base, const i2c_slave_config_t *slaveConfig, uint32_t srcClock_Hz) +{ + status_t status; + uint32_t divVal = 0; + + /* configure data setup time used when slave stretches clock */ + status = I2C_SlaveDivVal(srcClock_Hz, slaveConfig->busSpeed, &divVal); + if (kStatus_Success != status) + { + return status; + } + + (void)FLEXCOMM_Init(base, FLEXCOMM_PERIPH_I2C); + + /* I2C Clock Divider register */ + base->CLKDIV = divVal; + + /* set Slave address */ + I2C_SlaveSetAddress(base, kI2C_SlaveAddressRegister0, slaveConfig->address0.address, + slaveConfig->address0.addressDisable); + I2C_SlaveSetAddress(base, kI2C_SlaveAddressRegister1, slaveConfig->address1.address, + slaveConfig->address1.addressDisable); + I2C_SlaveSetAddress(base, kI2C_SlaveAddressRegister2, slaveConfig->address2.address, + slaveConfig->address2.addressDisable); + I2C_SlaveSetAddress(base, kI2C_SlaveAddressRegister3, slaveConfig->address3.address, + slaveConfig->address3.addressDisable); + + /* set Slave address 0 qual */ + base->SLVQUAL0 = I2C_SLVQUAL0_QUALMODE0(slaveConfig->qualMode) | I2C_SLVQUAL0_SLVQUAL0(slaveConfig->qualAddress); + + /* set Slave enable */ + base->CFG = I2C_CFG_SLVEN(slaveConfig->enableSlave); + + return status; +} + +/*! + * brief Deinitializes the I2C slave peripheral. + * + * This function disables the I2C slave peripheral and gates the clock. It also performs a software + * reset to restore the peripheral to reset conditions. + * + * param base The I2C peripheral base address. + */ +void I2C_SlaveDeinit(I2C_Type *base) +{ + I2C_SlaveEnable(base, false); +} + +/*! + * brief Performs a polling send transfer on the I2C bus. + * + * The function executes blocking address phase and blocking data phase. + * + * param base The I2C peripheral base address. + * param txBuff The pointer to the data to be transferred. + * param txSize The length in bytes of the data to be transferred. + * return kStatus_Success Data has been sent. + * return kStatus_Fail Unexpected slave state (master data write while master read from slave is expected). + */ +status_t I2C_SlaveWriteBlocking(I2C_Type *base, const uint8_t *txBuff, size_t txSize) +{ + const uint8_t *buf = txBuff; + uint32_t stat; + bool slaveAddress; + bool slaveTransmit; + + /* Set the SLVEN bit to 1 in the CFG register. */ + I2C_SlaveEnable(base, true); + + /* wait for SLVPENDING */ + stat = I2C_SlavePollPending(base); + if (stat == (uint32_t)kStatus_I2C_Timeout) + { + return kStatus_I2C_Timeout; + } + + /* Get slave machine state */ + slaveAddress = (((stat & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) == (uint32_t)I2C_STAT_SLVST_ADDR); + slaveTransmit = (((stat & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) == (uint32_t)I2C_STAT_SLVST_TX); + + /* in I2C_SlaveSend() it shall be either slaveAddress or slaveTransmit */ + if (!(slaveAddress || slaveTransmit)) + { + I2C_SlaveInternalStateMachineReset(base); + return kStatus_Fail; + } + + if (slaveAddress) + { + /* Acknowledge (ack) the address by setting SLVCONTINUE = 1 in the slave control register */ + base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK; + + /* wait for SLVPENDING */ + stat = I2C_SlavePollPending(base); + if (stat == (uint32_t)kStatus_I2C_Timeout) + { + return kStatus_I2C_Timeout; + } + } + + /* send bytes up to txSize */ + while (txSize != 0U) + { + slaveTransmit = (((stat & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) == (uint32_t)I2C_STAT_SLVST_TX); + + if (!slaveTransmit) + { + I2C_SlaveInternalStateMachineReset(base); + return kStatus_Fail; + } + + /* Write 8 bits of data to the SLVDAT register */ + base->SLVDAT = I2C_SLVDAT_DATA(*buf); + + /* continue transaction */ + base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK; + + /* advance counters and pointers for next data */ + buf++; + txSize--; + + if (txSize != 0U) + { + /* wait for SLVPENDING */ + stat = I2C_SlavePollPending(base); + if (stat == (uint32_t)kStatus_I2C_Timeout) + { + return kStatus_I2C_Timeout; + } + } + } + + return kStatus_Success; +} + +/*! + * brief Performs a polling receive transfer on the I2C bus. + * + * The function executes blocking address phase and blocking data phase. + * + * param base The I2C peripheral base address. + * param rxBuff The pointer to the data to be transferred. + * param rxSize The length in bytes of the data to be transferred. + * return kStatus_Success Data has been received. + * return kStatus_Fail Unexpected slave state (master data read while master write to slave is expected). + */ +status_t I2C_SlaveReadBlocking(I2C_Type *base, uint8_t *rxBuff, size_t rxSize) +{ + uint8_t *buf = rxBuff; + uint32_t stat; + bool slaveAddress; + bool slaveReceive; + + /* Set the SLVEN bit to 1 in the CFG register. */ + I2C_SlaveEnable(base, true); + + /* wait for SLVPENDING */ + stat = I2C_SlavePollPending(base); + if (stat == (uint32_t)kStatus_I2C_Timeout) + { + return kStatus_I2C_Timeout; + } + + /* Get slave machine state */ + slaveAddress = (((stat & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) == (uint32_t)I2C_STAT_SLVST_ADDR); + slaveReceive = (((stat & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) == (uint32_t)I2C_STAT_SLVST_RX); + + /* in I2C_SlaveReceive() it shall be either slaveAddress or slaveReceive */ + if (!(slaveAddress || slaveReceive)) + { + I2C_SlaveInternalStateMachineReset(base); + return kStatus_Fail; + } + + if (slaveAddress) + { + /* Acknowledge (ack) the address by setting SLVCONTINUE = 1 in the slave control register */ + base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK; + + /* wait for SLVPENDING */ + stat = I2C_SlavePollPending(base); + if (stat == (uint32_t)kStatus_I2C_Timeout) + { + return kStatus_I2C_Timeout; + } + } + + /* receive bytes up to rxSize */ + while (rxSize != 0U) + { + slaveReceive = (((stat & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) == (uint32_t)I2C_STAT_SLVST_RX); + + if (!slaveReceive) + { + I2C_SlaveInternalStateMachineReset(base); + return kStatus_Fail; + } + + /* Read 8 bits of data from the SLVDAT register */ + *buf = (uint8_t)base->SLVDAT; + + /* continue transaction */ + base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK; + + /* advance counters and pointers for next data */ + buf++; + rxSize--; + + if (rxSize != 0U) + { + /* wait for SLVPENDING */ + stat = I2C_SlavePollPending(base); + if (stat == (uint32_t)kStatus_I2C_Timeout) + { + return kStatus_I2C_Timeout; + } + } + } + + return kStatus_Success; +} + +/*! + * brief Creates a new handle for the I2C slave non-blocking APIs. + * + * The creation of a handle is for use with the non-blocking APIs. Once a handle + * is created, there is not a corresponding destroy handle. If the user wants to + * terminate a transfer, the I2C_SlaveTransferAbort() API shall be called. + * + * param base The I2C peripheral base address. + * param[out] handle Pointer to the I2C slave driver handle. + * param callback User provided pointer to the asynchronous callback function. + * param userData User provided pointer to the application callback data. + */ +void I2C_SlaveTransferCreateHandle(I2C_Type *base, + i2c_slave_handle_t *handle, + i2c_slave_transfer_callback_t callback, + void *userData) +{ + assert(handle != NULL); + + uint32_t instance; + i2c_to_flexcomm_t handler; + handler.i2c_slave_handler = I2C_SlaveTransferHandleIRQ; + + /* Clear out the handle. */ + (void)memset(handle, 0, sizeof(*handle)); + + /* Look up instance number */ + instance = I2C_GetInstance(base); + + /* Save base and instance. */ + handle->callback = callback; + handle->userData = userData; + + /* initialize fsm */ + handle->slaveFsm = kI2C_SlaveFsmAddressMatch; + + /* store pointer to handle into transfer struct */ + handle->transfer.handle = handle; + + FLEXCOMM_SetIRQHandler(base, handler.flexcomm_handler, handle); + + /* Clear internal IRQ enables and enable NVIC IRQ. */ + I2C_DisableInterrupts(base, (uint32_t)kI2C_SlaveIrqFlags); + (void)EnableIRQ(s_i2cIRQ[instance]); +} + +/*! + * brief Starts accepting slave transfers. + * + * Call this API after calling I2C_SlaveInit() and I2C_SlaveTransferCreateHandle() to start processing + * transactions driven by an I2C master. The slave monitors the I2C bus and pass events to the + * callback that was passed into the call to I2C_SlaveTransferCreateHandle(). The callback is always invoked + * from the interrupt context. + * + * If no slave Tx transfer is busy, a master read from slave request invokes #kI2C_SlaveTransmitEvent callback. + * If no slave Rx transfer is busy, a master write to slave request invokes #kI2C_SlaveReceiveEvent callback. + * + * The set of events received by the callback is customizable. To do so, set the a eventMask parameter to + * the OR'd combination of #i2c_slave_transfer_event_t enumerators for the events you wish to receive. + * The #kI2C_SlaveTransmitEvent and #kI2C_SlaveReceiveEvent events are always enabled and do not need + * to be included in the mask. Alternatively, you can pass 0 to get a default set of only the transmit and + * receive events that are always enabled. In addition, the #kI2C_SlaveAllEvents constant is provided as + * a convenient way to enable all events. + * + * param base The I2C peripheral base address. + * param handle Pointer to i2c_slave_handle_t structure which stores the transfer state. + * param eventMask Bit mask formed by OR'ing together #i2c_slave_transfer_event_t enumerators to specify + * which events to send to the callback. Other accepted values are 0 to get a default set of + * only the transmit and receive events, and #kI2C_SlaveAllEvents to enable all events. + * + * retval kStatus_Success Slave transfers were successfully started. + * retval #kStatus_I2C_Busy Slave transfers have already been started on this handle. + */ +status_t I2C_SlaveTransferNonBlocking(I2C_Type *base, i2c_slave_handle_t *handle, uint32_t eventMask) +{ + return I2C_SlaveTransferNonBlockingInternal(base, handle, NULL, 0u, NULL, 0u, eventMask); +} + +/*! + * brief Gets the slave transfer remaining bytes during a interrupt non-blocking transfer. + * + * param base I2C base pointer. + * param handle pointer to i2c_slave_handle_t structure. + * param count Number of bytes transferred so far by the non-blocking transaction. + * retval kStatus_InvalidArgument count is Invalid. + * retval kStatus_Success Successfully return the count. + */ +status_t I2C_SlaveTransferGetCount(I2C_Type *base, i2c_slave_handle_t *handle, size_t *count) +{ + assert(handle != NULL); + + if (NULL == count) + { + return kStatus_InvalidArgument; + } + + /* Catch when there is not an active transfer. */ + if (!handle->isBusy) + { + *count = 0; + return kStatus_NoTransferInProgress; + } + + /* For an active transfer, just return the count from the handle. */ + *count = handle->transfer.transferredCount; + + return kStatus_Success; +} + +/*! + * brief Aborts the slave non-blocking transfers. + * note This API could be called at any time to stop slave for handling the bus events. + * param base The I2C peripheral base address. + * param handle Pointer to i2c_slave_handle_t structure which stores the transfer state. + * retval kStatus_Success + * retval #kStatus_I2C_Idle + */ +void I2C_SlaveTransferAbort(I2C_Type *base, i2c_slave_handle_t *handle) +{ + /* Disable I2C IRQ sources while we configure stuff. */ + I2C_DisableInterrupts(base, (uint32_t)kI2C_SlaveIrqFlags); + + /* Set the SLVEN bit to 0 in the CFG register. */ + I2C_SlaveEnable(base, false); + + handle->isBusy = false; + handle->transfer.txSize = 0U; + handle->transfer.rxSize = 0U; +} + +/*! + * brief Reusable routine to handle slave interrupts. + * note This function does not need to be called unless you are reimplementing the + * non blocking API's interrupt handler routines to add special functionality. + * param base The I2C peripheral base address. + * param handle Pointer to i2c_slave_handle_t structure which stores the transfer state. + */ +void I2C_SlaveTransferHandleIRQ(I2C_Type *base, i2c_slave_handle_t *handle) +{ + uint32_t i2cStatus = base->STAT; + uint8_t tmpdata; + size_t txSize; + size_t rxSize; + + if ((i2cStatus & I2C_STAT_SLVDESEL_MASK) != 0U) + { + I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveDeselectedEvent); + I2C_SlaveClearStatusFlags(base, I2C_STAT_SLVDESEL_MASK); + } + + /* SLVPENDING flag is cleared by writing I2C_SLVCTL_SLVCONTINUE_MASK to SLVCTL register */ + if ((i2cStatus & I2C_STAT_SLVPENDING_MASK) != 0U) + { + bool slaveAddress = + (((i2cStatus & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) == (uint32_t)I2C_STAT_SLVST_ADDR); + + if (slaveAddress) + { + I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveAddressMatchEvent); + (void)I2C_SlaveAddressIRQ(base, handle); + } + else + { + switch (handle->slaveFsm) + { + case kI2C_SlaveFsmReceive: + { + bool slaveReceive = (((i2cStatus & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) == + (uint32_t)I2C_STAT_SLVST_RX); + + if (slaveReceive) + { + rxSize = handle->transfer.rxSize; + /* if we have no receive buffer in this transfer, call callback to get new */ + if ((handle->transfer.rxData == NULL) || (rxSize == 0U)) + { + I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveReceiveEvent); + } + + rxSize = handle->transfer.rxSize; + /* receive a byte */ + if ((handle->transfer.rxData != NULL) && (rxSize != 0U)) + { + /* continue transaction */ + base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK; + tmpdata = (uint8_t)base->SLVDAT; + *(handle->transfer.rxData) = tmpdata; + (handle->transfer.rxSize)--; + (handle->transfer.rxData)++; + (handle->transfer.transferredCount)++; + } + + rxSize = handle->transfer.rxSize; + txSize = handle->transfer.txSize; + /* is this last transaction for this transfer? allow next transaction */ + if ((0U == rxSize) && (0U == txSize)) + { + handle->isBusy = false; + I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveCompletionEvent); + } + } + else + { + base->SLVCTL = I2C_SLVCTL_SLVNACK_MASK; + } + } + break; + + case kI2C_SlaveFsmTransmit: + { + bool slaveTransmit = (((i2cStatus & I2C_STAT_SLVSTATE_MASK) >> I2C_STAT_SLVSTATE_SHIFT) == + (uint32_t)I2C_STAT_SLVST_TX); + + if (slaveTransmit) + { + txSize = handle->transfer.txSize; + /* if we have no data in this transfer, call callback to get new */ + if ((handle->transfer.txData == NULL) || (txSize == 0U)) + { + I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveTransmitEvent); + } + + txSize = handle->transfer.txSize; + /* transmit a byte */ + if ((handle->transfer.txData != NULL) && (txSize != 0U)) + { + base->SLVDAT = *(handle->transfer.txData); + /* continue transaction */ + base->SLVCTL = I2C_SLVCTL_SLVCONTINUE_MASK; + (handle->transfer.txSize)--; + (handle->transfer.txData)++; + (handle->transfer.transferredCount)++; + } + + rxSize = handle->transfer.rxSize; + txSize = handle->transfer.txSize; + /* is this last transaction for this transfer? allow next transaction */ + if ((0U == rxSize) && (0U == txSize)) + { + handle->isBusy = false; + I2C_SlaveInvokeEvent(base, handle, kI2C_SlaveCompletionEvent); + } + } + else + { + base->SLVCTL = I2C_SLVCTL_SLVNACK_MASK; + } + } + break; + + default: + /* incorrect state, slv_abort()? */ + break; + } + } + } +} diff --git a/drivers/fsl_i2c.h b/drivers/fsl_i2c.h new file mode 100644 index 0000000..5e5c511 --- /dev/null +++ b/drivers/fsl_i2c.h @@ -0,0 +1,1148 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_I2C_H_ +#define _FSL_I2C_H_ + +#include +#include "fsl_device_registers.h" +#include "fsl_common.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +#define I2C_CFG_MASK 0x1f + +/*! + * @addtogroup i2c_driver + * @{ + */ + +/*! @file */ + +/*! @name Driver version */ +/*@{*/ +/*! @brief I2C driver version. */ +#define FSL_I2C_DRIVER_VERSION (MAKE_VERSION(2, 3, 0)) +/*@}*/ + +/*! @brief Retry times for waiting flag. */ +#ifndef I2C_RETRY_TIMES +#define I2C_RETRY_TIMES 0U /* Define to zero means keep waiting until the flag is assert/deassert. */ +#endif + +/*! @brief Whether to ignore the nack signal of the last byte during master transmit. */ +#ifndef I2C_MASTER_TRANSMIT_IGNORE_LAST_NACK +#define I2C_MASTER_TRANSMIT_IGNORE_LAST_NACK \ + 1U /* Define to one means master ignores the last byte's nack and considers the transfer successful. */ +#endif + +/* definitions for MSTCODE bits in I2C Status register STAT */ +#define I2C_STAT_MSTCODE_IDLE (0U) /*!< Master Idle State Code */ +#define I2C_STAT_MSTCODE_RXREADY (1U) /*!< Master Receive Ready State Code */ +#define I2C_STAT_MSTCODE_TXREADY (2U) /*!< Master Transmit Ready State Code */ +#define I2C_STAT_MSTCODE_NACKADR (3U) /*!< Master NACK by slave on address State Code */ +#define I2C_STAT_MSTCODE_NACKDAT (4U) /*!< Master NACK by slave on data State Code */ + +/* definitions for SLVSTATE bits in I2C Status register STAT */ +#define I2C_STAT_SLVST_ADDR (0) +#define I2C_STAT_SLVST_RX (1) +#define I2C_STAT_SLVST_TX (2) + +/*! @brief I2C status return codes. */ +enum +{ + kStatus_I2C_Busy = MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 0), /*!< The master is already performing a transfer. */ + kStatus_I2C_Idle = MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 1), /*!< The slave driver is idle. */ + kStatus_I2C_Nak = + MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 2), /*!< The slave device sent a NAK in response to a byte. */ + kStatus_I2C_InvalidParameter = + MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 3), /*!< Unable to proceed due to invalid parameter. */ + kStatus_I2C_BitError = MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 4), /*!< Transferred bit was not seen on the bus. */ + kStatus_I2C_ArbitrationLost = MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 5), /*!< Arbitration lost error. */ + kStatus_I2C_NoTransferInProgress = + MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 6), /*!< Attempt to abort a transfer when one is not in progress. */ + kStatus_I2C_DmaRequestFail = MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 7), /*!< DMA request failed. */ + kStatus_I2C_StartStopError = MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 8), /*!< Start and stop error. */ + kStatus_I2C_UnexpectedState = MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 9), /*!< Unexpected state. */ + kStatus_I2C_Timeout = + MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, + 10), /*!< Timeout when waiting for I2C master/slave pending status to set to continue transfer. */ + kStatus_I2C_Addr_Nak = MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 11), /*!< NAK received for Address */ + kStatus_I2C_EventTimeout = MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 12), /*!< Timeout waiting for bus event. */ + kStatus_I2C_SclLowTimeout = MAKE_STATUS(kStatusGroup_FLEXCOMM_I2C, 13), /*!< Timeout SCL signal remains low. */ +}; + +/*! @} */ + +/*! + * @addtogroup i2c_driver + * @{ + */ + +/*! + * @brief I2C status flags. + * + * @note These enums are meant to be OR'd together to form a bit mask. + */ +enum _i2c_status_flags +{ + kI2C_MasterPendingFlag = I2C_STAT_MSTPENDING_MASK, /*!< The I2C module is waiting for software interaction. bit 0 */ + kI2C_MasterArbitrationLostFlag = + I2C_STAT_MSTARBLOSS_MASK, /*!< The arbitration of the bus was lost. There was collision on the bus. bit 4*/ + kI2C_MasterStartStopErrorFlag = + I2C_STAT_MSTSTSTPERR_MASK, /*!< There was an error during start or stop phase of the transaction. bit 6 */ + kI2C_MasterIdleFlag = 1UL << 5U, /*!< The I2C master idle status. bit 5 */ + kI2C_MasterRxReadyFlag = 1UL << I2C_STAT_MSTSTATE_SHIFT, /*!< The I2C master rx ready status. bit 1 */ + kI2C_MasterTxReadyFlag = 1UL << (I2C_STAT_MSTSTATE_SHIFT + 1U), /*!< The I2C master tx ready status. bit 2 */ + kI2C_MasterAddrNackFlag = 1UL << 7U, /*!< The I2C master address nack status. bit 7 */ + kI2C_MasterDataNackFlag = 1UL << (I2C_STAT_MSTSTATE_SHIFT + 2U), /*!< The I2C master data nack status. bit 3 */ + kI2C_SlavePendingFlag = I2C_STAT_SLVPENDING_MASK, /*!< The I2C module is waiting for software interaction. bit 8 */ + kI2C_SlaveNotStretching = I2C_STAT_SLVNOTSTR_MASK, /*!< Indicates whether the slave is currently stretching clock (0 + = yes, 1 = no). bit 11 */ + kI2C_SlaveSelected = + I2C_STAT_SLVSEL_MASK, /*!< Indicates whether the slave is selected by an address match. bit 14 */ + kI2C_SaveDeselected = I2C_STAT_SLVDESEL_MASK, /*!< Indicates that slave was previously deselected (deselect event + took place, w1c). bit 15 */ + kI2C_SlaveAddressedFlag = 1UL << 22U, /*!< One of the I2C slave's 4 addresses is matched. bit 22 */ + kI2C_SlaveReceiveFlag = 1UL << I2C_STAT_SLVSTATE_SHIFT, /*!< Slave receive data available. bit 9 */ + kI2C_SlaveTransmitFlag = 1UL << (I2C_STAT_SLVSTATE_SHIFT + 1U), /*!< Slave data can be transmitted. bit 10 */ + kI2C_SlaveAddress0MatchFlag = 1UL << 20U, /*!< Slave address0 match. bit 20 */ + kI2C_SlaveAddress1MatchFlag = 1UL << I2C_STAT_SLVIDX_SHIFT, /*!< Slave address1 match. bit 12 */ + kI2C_SlaveAddress2MatchFlag = 1UL << (I2C_STAT_SLVIDX_SHIFT + 1U), /*!< Slave address2 match. bit 13 */ + kI2C_SlaveAddress3MatchFlag = 1UL << 21U, /*!< Slave address3 match. bit 21 */ + kI2C_MonitorReadyFlag = I2C_STAT_MONRDY_MASK, /*!< The I2C monitor ready interrupt. bit 16 */ + kI2C_MonitorOverflowFlag = I2C_STAT_MONOV_MASK, /*!< The monitor data overrun interrupt. bit 17 */ + kI2C_MonitorActiveFlag = I2C_STAT_MONACTIVE_MASK, /*!< The monitor is active. bit 18 */ + kI2C_MonitorIdleFlag = I2C_STAT_MONIDLE_MASK, /*!< The monitor idle interrupt. bit 19 */ + kI2C_EventTimeoutFlag = I2C_STAT_EVENTTIMEOUT_MASK, /*!< The bus event timeout interrupt. bit 24 */ + kI2C_SclTimeoutFlag = I2C_STAT_SCLTIMEOUT_MASK, /*!< The SCL timeout interrupt. bit 25 */ + + /* All master flags that can be cleared by software */ + kI2C_MasterAllClearFlags = kI2C_MasterArbitrationLostFlag | kI2C_MasterStartStopErrorFlag, + /* All slave flags that can be cleared by software */ + kI2C_SlaveAllClearFlags = kI2C_SaveDeselected, + /* All common flags that can be cleared by software */ + kI2C_CommonAllClearFlags = + kI2C_MonitorOverflowFlag | kI2C_MonitorIdleFlag | kI2C_EventTimeoutFlag | kI2C_SclTimeoutFlag, +}; + +/*! + * @brief I2C interrupt enable. + * + * @note These enums are meant to be OR'd together to form a bit mask. + */ +enum _i2c_interrupt_enable +{ + kI2C_MasterPendingInterruptEnable = + I2C_STAT_MSTPENDING_MASK, /*!< The I2C master communication pending interrupt. */ + kI2C_MasterArbitrationLostInterruptEnable = + I2C_STAT_MSTARBLOSS_MASK, /*!< The I2C master arbitration lost interrupt. */ + kI2C_MasterStartStopErrorInterruptEnable = + I2C_STAT_MSTSTSTPERR_MASK, /*!< The I2C master start/stop timing error interrupt. */ + kI2C_SlavePendingInterruptEnable = I2C_STAT_SLVPENDING_MASK, /*!< The I2C slave communication pending interrupt. */ + kI2C_SlaveNotStretchingInterruptEnable = + I2C_STAT_SLVNOTSTR_MASK, /*!< The I2C slave not streching interrupt, deep-sleep mode can be entered only when + this interrupt occurs. */ + kI2C_SlaveDeselectedInterruptEnable = I2C_STAT_SLVDESEL_MASK, /*!< The I2C slave deselection interrupt. */ + kI2C_MonitorReadyInterruptEnable = I2C_STAT_MONRDY_MASK, /*!< The I2C monitor ready interrupt. */ + kI2C_MonitorOverflowInterruptEnable = I2C_STAT_MONOV_MASK, /*!< The monitor data overrun interrupt. */ + kI2C_MonitorIdleInterruptEnable = I2C_STAT_MONIDLE_MASK, /*!< The monitor idle interrupt. */ + kI2C_EventTimeoutInterruptEnable = I2C_STAT_EVENTTIMEOUT_MASK, /*!< The bus event timeout interrupt. */ + kI2C_SclTimeoutInterruptEnable = I2C_STAT_SCLTIMEOUT_MASK, /*!< The SCL timeout interrupt. */ + + /* All master interrupt sources */ + kI2C_MasterAllInterruptEnable = kI2C_MasterPendingInterruptEnable | kI2C_MasterArbitrationLostInterruptEnable | + kI2C_MasterStartStopErrorInterruptEnable, + /* All slave interrupt sources */ + kI2C_SlaveAllInterruptEnable = + kI2C_SlavePendingInterruptEnable | kI2C_SlaveNotStretchingInterruptEnable | kI2C_SlaveDeselectedInterruptEnable, + /* All common interrupt sources */ + kI2C_CommonAllInterruptEnable = kI2C_MonitorReadyInterruptEnable | kI2C_MonitorOverflowInterruptEnable | + kI2C_MonitorIdleInterruptEnable | kI2C_EventTimeoutInterruptEnable | + kI2C_SclTimeoutInterruptEnable, +}; +/*! @} */ + +/*! + * @addtogroup i2c_master_driver + * @{ + */ + +/*! @brief Direction of master and slave transfers. */ +typedef enum _i2c_direction +{ + kI2C_Write = 0U, /*!< Master transmit. */ + kI2C_Read = 1U /*!< Master receive. */ +} i2c_direction_t; + +/*! + * @brief Structure with settings to initialize the I2C master module. + * + * This structure holds configuration settings for the I2C peripheral. To initialize this + * structure to reasonable defaults, call the I2C_MasterGetDefaultConfig() function and + * pass a pointer to your configuration structure instance. + * + * The configuration structure can be made constant so it resides in flash. + */ +typedef struct _i2c_master_config +{ + bool enableMaster; /*!< Whether to enable master mode. */ + uint32_t baudRate_Bps; /*!< Desired baud rate in bits per second. */ + bool enableTimeout; /*!< Enable internal timeout function. */ + uint8_t timeout_Ms; /*!< Event timeout and SCL low timeout value. */ +} i2c_master_config_t; + +/* Forward declaration of the transfer descriptor and handle typedefs. */ +/*! @brief I2C master transfer typedef */ +typedef struct _i2c_master_transfer i2c_master_transfer_t; + +/*! @brief I2C master handle typedef */ +typedef struct _i2c_master_handle i2c_master_handle_t; + +/*! + * @brief Master completion callback function pointer type. + * + * This callback is used only for the non-blocking master transfer API. Specify the callback you wish to use + * in the call to I2C_MasterTransferCreateHandle(). + * + * @param base The I2C peripheral base address. + * @param completionStatus Either kStatus_Success or an error code describing how the transfer completed. + * @param userData Arbitrary pointer-sized value passed from the application. + */ +typedef void (*i2c_master_transfer_callback_t)(I2C_Type *base, + i2c_master_handle_t *handle, + status_t completionStatus, + void *userData); + +/*! + * @brief Transfer option flags. + * + * @note These enumerations are intended to be OR'd together to form a bit mask of options for + * the #_i2c_master_transfer::flags field. + */ +enum _i2c_master_transfer_flags +{ + kI2C_TransferDefaultFlag = 0x00U, /*!< Transfer starts with a start signal, stops with a stop signal. */ + kI2C_TransferNoStartFlag = 0x01U, /*!< Don't send a start condition, address, and sub address */ + kI2C_TransferRepeatedStartFlag = 0x02U, /*!< Send a repeated start condition */ + kI2C_TransferNoStopFlag = 0x04U, /*!< Don't send a stop condition. */ +}; + +/*! @brief States for the state machine used by transactional APIs. */ +enum _i2c_transfer_states +{ + kIdleState = 0, + kTransmitSubaddrState, + kTransmitDataState, + kReceiveDataBeginState, + kReceiveDataState, + kReceiveLastDataState, + kStartState, + kStopState, + kWaitForCompletionState +}; + +/*! + * @brief Non-blocking transfer descriptor structure. + * + * This structure is used to pass transaction parameters to the I2C_MasterTransferNonBlocking() API. + */ +struct _i2c_master_transfer +{ + uint32_t flags; /*!< Bit mask of options for the transfer. See enumeration #_i2c_master_transfer_flags for available + options. Set to 0 or #kI2C_TransferDefaultFlag for normal transfers. */ + uint8_t slaveAddress; /*!< The 7-bit slave address. */ + i2c_direction_t direction; /*!< Either #kI2C_Read or #kI2C_Write. */ + uint32_t subaddress; /*!< Sub address. Transferred MSB first. */ + size_t subaddressSize; /*!< Length of sub address to send in bytes. Maximum size is 4 bytes. */ + void *data; /*!< Pointer to data to transfer. */ + size_t dataSize; /*!< Number of bytes to transfer. */ +}; + +/*! + * @brief Driver handle for master non-blocking APIs. + * @note The contents of this structure are private and subject to change. + */ +struct _i2c_master_handle +{ + uint8_t state; /*!< Transfer state machine current state. */ + uint32_t transferCount; /*!< Indicates progress of the transfer */ + uint32_t remainingBytes; /*!< Remaining byte count in current state. */ + uint8_t *buf; /*!< Buffer pointer for current state. */ + uint32_t remainingSubaddr; + uint8_t subaddrBuf[4]; + bool checkAddrNack; /*!< Whether to check the nack signal is detected during addressing. */ + i2c_master_transfer_t transfer; /*!< Copy of the current transfer info. */ + i2c_master_transfer_callback_t completionCallback; /*!< Callback function pointer. */ + void *userData; /*!< Application data passed to callback. */ +}; + +/*! @} */ + +/*! + * @addtogroup i2c_slave_driver + * @{ + */ +/*! @brief I2C slave address register. */ +typedef enum _i2c_slave_address_register +{ + kI2C_SlaveAddressRegister0 = 0U, /*!< Slave Address 0 register. */ + kI2C_SlaveAddressRegister1 = 1U, /*!< Slave Address 1 register. */ + kI2C_SlaveAddressRegister2 = 2U, /*!< Slave Address 2 register. */ + kI2C_SlaveAddressRegister3 = 3U, /*!< Slave Address 3 register. */ +} i2c_slave_address_register_t; + +/*! @brief Data structure with 7-bit Slave address and Slave address disable. */ +typedef struct _i2c_slave_address +{ + uint8_t address; /*!< 7-bit Slave address SLVADR. */ + bool addressDisable; /*!< Slave address disable SADISABLE. */ +} i2c_slave_address_t; + +/*! @brief I2C slave address match options. */ +typedef enum _i2c_slave_address_qual_mode +{ + kI2C_QualModeMask = 0U, /*!< The SLVQUAL0 field (qualAddress) is used as a logical mask for matching address0. */ + kI2C_QualModeExtend = + 1U, /*!< The SLVQUAL0 (qualAddress) field is used to extend address 0 matching in a range of addresses. */ +} i2c_slave_address_qual_mode_t; + +/*! @brief I2C slave bus speed options. */ +typedef enum _i2c_slave_bus_speed +{ + kI2C_SlaveStandardMode = 0U, + kI2C_SlaveFastMode = 1U, + kI2C_SlaveFastModePlus = 2U, + kI2C_SlaveHsMode = 3U, +} i2c_slave_bus_speed_t; + +/*! + * @brief Structure with settings to initialize the I2C slave module. + * + * This structure holds configuration settings for the I2C slave peripheral. To initialize this + * structure to reasonable defaults, call the I2C_SlaveGetDefaultConfig() function and + * pass a pointer to your configuration structure instance. + * + * The configuration structure can be made constant so it resides in flash. + */ +typedef struct _i2c_slave_config +{ + i2c_slave_address_t address0; /*!< Slave's 7-bit address and disable. */ + i2c_slave_address_t address1; /*!< Alternate slave 7-bit address and disable. */ + i2c_slave_address_t address2; /*!< Alternate slave 7-bit address and disable. */ + i2c_slave_address_t address3; /*!< Alternate slave 7-bit address and disable. */ + i2c_slave_address_qual_mode_t qualMode; /*!< Qualify mode for slave address 0. */ + uint8_t qualAddress; /*!< Slave address qualifier for address 0. */ + i2c_slave_bus_speed_t + busSpeed; /*!< Slave bus speed mode. If the slave function stretches SCL to allow for software response, it must + provide sufficient data setup time to the master before releasing the stretched clock. + This is accomplished by inserting one clock time of CLKDIV at that point. + The #busSpeed value is used to configure CLKDIV + such that one clock time is greater than the tSU;DAT value noted + in the I2C bus specification for the I2C mode that is being used. + If the #busSpeed mode is unknown at compile time, use the longest data setup time + kI2C_SlaveStandardMode (250 ns) */ + bool enableSlave; /*!< Enable slave mode. */ +} i2c_slave_config_t; + +/*! + * @brief Set of events sent to the callback for non blocking slave transfers. + * + * These event enumerations are used for two related purposes. First, a bit mask created by OR'ing together + * events is passed to I2C_SlaveTransferNonBlocking() in order to specify which events to enable. + * Then, when the slave callback is invoked, it is passed the current event through its @a transfer + * parameter. + * + * @note These enumerations are meant to be OR'd together to form a bit mask of events. + */ +typedef enum _i2c_slave_transfer_event +{ + kI2C_SlaveAddressMatchEvent = 0x01U, /*!< Received the slave address after a start or repeated start. */ + kI2C_SlaveTransmitEvent = 0x02U, /*!< Callback is requested to provide data to transmit + (slave-transmitter role). */ + kI2C_SlaveReceiveEvent = 0x04U, /*!< Callback is requested to provide a buffer in which to place received + data (slave-receiver role). */ + kI2C_SlaveCompletionEvent = 0x20U, /*!< All data in the active transfer have been consumed. */ + kI2C_SlaveDeselectedEvent = + 0x40U, /*!< The slave function has become deselected (SLVSEL flag changing from 1 to 0. */ + + /*! Bit mask of all available events. */ + kI2C_SlaveAllEvents = kI2C_SlaveAddressMatchEvent | kI2C_SlaveTransmitEvent | kI2C_SlaveReceiveEvent | + kI2C_SlaveCompletionEvent | kI2C_SlaveDeselectedEvent, +} i2c_slave_transfer_event_t; + +/*! @brief I2C slave handle typedef. */ +typedef struct _i2c_slave_handle i2c_slave_handle_t; + +/*! @brief I2C slave transfer structure */ +typedef struct _i2c_slave_transfer +{ + i2c_slave_handle_t *handle; /*!< Pointer to handle that contains this transfer. */ + i2c_slave_transfer_event_t event; /*!< Reason the callback is being invoked. */ + uint8_t receivedAddress; /*!< Matching address send by master. 7-bits plus R/nW bit0 */ + uint32_t eventMask; /*!< Mask of enabled events. */ + uint8_t *rxData; /*!< Transfer buffer for receive data */ + const uint8_t *txData; /*!< Transfer buffer for transmit data */ + size_t txSize; /*!< Transfer size */ + size_t rxSize; /*!< Transfer size */ + size_t transferredCount; /*!< Number of bytes transferred during this transfer. */ + status_t completionStatus; /*!< Success or error code describing how the transfer completed. Only applies for + #kI2C_SlaveCompletionEvent. */ +} i2c_slave_transfer_t; + +/*! + * @brief Slave event callback function pointer type. + * + * This callback is used only for the slave non-blocking transfer API. To install a callback, + * use the I2C_SlaveSetCallback() function after you have created a handle. + * + * @param base Base address for the I2C instance on which the event occurred. + * @param transfer Pointer to transfer descriptor containing values passed to and/or from the callback. + * @param userData Arbitrary pointer-sized value passed from the application. + */ +typedef void (*i2c_slave_transfer_callback_t)(I2C_Type *base, volatile i2c_slave_transfer_t *transfer, void *userData); + +/*! + * @brief I2C slave software finite state machine states. + */ +typedef enum _i2c_slave_fsm +{ + kI2C_SlaveFsmAddressMatch = 0u, + kI2C_SlaveFsmReceive = 2u, + kI2C_SlaveFsmTransmit = 3u, +} i2c_slave_fsm_t; + +/*! + * @brief I2C slave handle structure. + * @note The contents of this structure are private and subject to change. + */ +struct _i2c_slave_handle +{ + volatile i2c_slave_transfer_t transfer; /*!< I2C slave transfer. */ + volatile bool isBusy; /*!< Whether transfer is busy. */ + volatile i2c_slave_fsm_t slaveFsm; /*!< slave transfer state machine. */ + i2c_slave_transfer_callback_t callback; /*!< Callback function called at transfer event. */ + void *userData; /*!< Callback parameter passed to callback. */ +}; + +/*! @brief Typedef for master interrupt handler. */ +typedef void (*flexcomm_i2c_master_irq_handler_t)(I2C_Type *base, i2c_master_handle_t *handle); + +/*! @brief Typedef for slave interrupt handler. */ +typedef void (*flexcomm_i2c_slave_irq_handler_t)(I2C_Type *base, i2c_slave_handle_t *handle); +/*! @} */ + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @addtogroup i2c_master_driver + * @{ + */ + +/*! @name Initialization and deinitialization */ +/*@{*/ + +/*! + * @brief Provides a default configuration for the I2C master peripheral. + * + * This function provides the following default configuration for the I2C master peripheral: + * @code + * masterConfig->enableMaster = true; + * masterConfig->baudRate_Bps = 100000U; + * masterConfig->enableTimeout = false; + * @endcode + * + * After calling this function, you can override any settings in order to customize the configuration, + * prior to initializing the master driver with I2C_MasterInit(). + * + * @param[out] masterConfig User provided configuration structure for default values. Refer to #i2c_master_config_t. + */ +void I2C_MasterGetDefaultConfig(i2c_master_config_t *masterConfig); + +/*! + * @brief Initializes the I2C master peripheral. + * + * This function enables the peripheral clock and initializes the I2C master peripheral as described by the user + * provided configuration. A software reset is performed prior to configuration. + * + * @param base The I2C peripheral base address. + * @param masterConfig User provided peripheral configuration. Use I2C_MasterGetDefaultConfig() to get a set of + * defaults + * that you can override. + * @param srcClock_Hz Frequency in Hertz of the I2C functional clock. Used to calculate the baud rate divisors, + * filter widths, and timeout periods. + */ +void I2C_MasterInit(I2C_Type *base, const i2c_master_config_t *masterConfig, uint32_t srcClock_Hz); + +/*! + * @brief Deinitializes the I2C master peripheral. + * + * This function disables the I2C master peripheral and gates the clock. It also performs a software + * reset to restore the peripheral to reset conditions. + * + * @param base The I2C peripheral base address. + */ +void I2C_MasterDeinit(I2C_Type *base); + +/*! + * @brief Returns an instance number given a base address. + * + * If an invalid base address is passed, debug builds will assert. Release builds will just return + * instance number 0. + * + * @param base The I2C peripheral base address. + * @return I2C instance number starting from 0. + */ +uint32_t I2C_GetInstance(I2C_Type *base); + +/*! + * @brief Performs a software reset. + * + * Restores the I2C master peripheral to reset conditions. + * + * @param base The I2C peripheral base address. + */ +static inline void I2C_MasterReset(I2C_Type *base) +{ +} + +/*! + * @brief Enables or disables the I2C module as master. + * + * @param base The I2C peripheral base address. + * @param enable Pass true to enable or false to disable the specified I2C as master. + */ +static inline void I2C_MasterEnable(I2C_Type *base, bool enable) +{ + if (enable) + { + base->CFG = (base->CFG & (uint32_t)I2C_CFG_MASK) | I2C_CFG_MSTEN_MASK; + } + else + { + base->CFG = (base->CFG & (uint32_t)I2C_CFG_MASK) & ~I2C_CFG_MSTEN_MASK; + } +} + +/*@}*/ + +/*! @name Status */ +/*@{*/ +/*! + * @brief Gets the I2C status flags. + * + * A bit mask with the state of all I2C status flags is returned. For each flag, the corresponding bit + * in the return value is set if the flag is asserted. + * + * @param base The I2C peripheral base address. + * @return State of the status flags: + * - 1: related status flag is set. + * - 0: related status flag is not set. + * @see @ref _i2c_status_flags. + */ +uint32_t I2C_GetStatusFlags(I2C_Type *base); + +/*! + * @brief Clears the I2C status flag state. + * + * Refer to kI2C_CommonAllClearStatusFlags, kI2C_MasterAllClearStatusFlags and kI2C_SlaveAllClearStatusFlags to see + * the clearable flags. Attempts to clear other flags has no effect. + * + * @param base The I2C peripheral base address. + * @param statusMask A bitmask of status flags that are to be cleared. The mask is composed of the members in + * kI2C_CommonAllClearStatusFlags, kI2C_MasterAllClearStatusFlags and kI2C_SlaveAllClearStatusFlags. You may pass + * the result of a previous call to I2C_GetStatusFlags(). + * @see #_i2c_status_flags, _i2c_master_status_flags and _i2c_slave_status_flags. + */ +static inline void I2C_ClearStatusFlags(I2C_Type *base, uint32_t statusMask) +{ + /* Only deal with the clearable flags */ + statusMask &= + ((uint32_t)kI2C_CommonAllClearFlags | (uint32_t)kI2C_MasterAllClearFlags | (uint32_t)kI2C_SlaveAllClearFlags); + base->STAT = statusMask; +} + +/*! + * @brief Clears the I2C master status flag state. + * @deprecated Do not use this function. It has been superceded by @ref I2C_ClearStatusFlags + * The following status register flags can be cleared: + * - #kI2C_MasterArbitrationLostFlag + * - #kI2C_MasterStartStopErrorFlag + * + * Attempts to clear other flags has no effect. + * + * @param base The I2C peripheral base address. + * @param statusMask A bitmask of status flags that are to be cleared. The mask is composed of + * #_i2c_status_flags enumerators OR'd together. You may pass the result of a previous call to + * I2C_GetStatusFlags(). + * @see _i2c_status_flags. + */ +static inline void I2C_MasterClearStatusFlags(I2C_Type *base, uint32_t statusMask) +{ + /* Allow clearing just master status flags */ + base->STAT = statusMask & (I2C_STAT_MSTARBLOSS_MASK | I2C_STAT_MSTSTSTPERR_MASK); +} + +/*@}*/ + +/*! @name Interrupts */ +/*@{*/ + +/*! + * @brief Enables the I2C interrupt requests. + * + * @param base The I2C peripheral base address. + * @param interruptMask Bit mask of interrupts to enable. See #_i2c_interrupt_enable for the set + * of constants that should be OR'd together to form the bit mask. + */ +static inline void I2C_EnableInterrupts(I2C_Type *base, uint32_t interruptMask) +{ + base->INTENSET = interruptMask; +} + +/*! + * @brief Disables the I2C interrupt requests. + * + * @param base The I2C peripheral base address. + * @param interruptMask Bit mask of interrupts to disable. See #_i2c_interrupt_enable for the set + * of constants that should be OR'd together to form the bit mask. + */ +static inline void I2C_DisableInterrupts(I2C_Type *base, uint32_t interruptMask) +{ + base->INTENCLR = interruptMask; +} + +/*! + * @brief Returns the set of currently enabled I2C interrupt requests. + * + * @param base The I2C peripheral base address. + * @return A bitmask composed of #_i2c_interrupt_enable enumerators OR'd together to indicate the + * set of enabled interrupts. + */ +static inline uint32_t I2C_GetEnabledInterrupts(I2C_Type *base) +{ + return base->INTSTAT; +} + +/*@}*/ + +/*! @name Bus operations */ +/*@{*/ + +/*! + * @brief Sets the I2C bus frequency for master transactions. + * + * The I2C master is automatically disabled and re-enabled as necessary to configure the baud + * rate. Do not call this function during a transfer, or the transfer is aborted. + * + * @param base The I2C peripheral base address. + * @param srcClock_Hz I2C functional clock frequency in Hertz. + * @param baudRate_Bps Requested bus frequency in bits per second. + */ +void I2C_MasterSetBaudRate(I2C_Type *base, uint32_t baudRate_Bps, uint32_t srcClock_Hz); + +/*! + * @brief Sets the I2C bus timeout value. + * + * If the SCL signal remains low or bus does not have event longer than the timeout value, kI2C_SclTimeoutFlag or + * kI2C_EventTimeoutFlag is set. This can indicete the bus is held by slave or any fault occurs to the I2C module. + * + * @param base The I2C peripheral base address. + * @param timeout_Ms Timeout value in millisecond. + * @param srcClock_Hz I2C functional clock frequency in Hertz. + */ +void I2C_MasterSetTimeoutValue(I2C_Type *base, uint8_t timeout_Ms, uint32_t srcClock_Hz); + +/*! + * @brief Returns whether the bus is idle. + * + * Requires the master mode to be enabled. + * + * @param base The I2C peripheral base address. + * @retval true Bus is busy. + * @retval false Bus is idle. + */ +static inline bool I2C_MasterGetBusIdleState(I2C_Type *base) +{ + /* True if MSTPENDING flag is set and MSTSTATE is zero == idle */ + return ((base->STAT & (I2C_STAT_MSTPENDING_MASK | I2C_STAT_MSTSTATE_MASK)) == I2C_STAT_MSTPENDING_MASK); +} + +/*! + * @brief Sends a START on the I2C bus. + * + * This function is used to initiate a new master mode transfer by sending the START signal. + * The slave address is sent following the I2C START signal. + * + * @param base I2C peripheral base pointer + * @param address 7-bit slave device address. + * @param direction Master transfer directions(transmit/receive). + * @retval kStatus_Success Successfully send the start signal. + * @retval kStatus_I2C_Busy Current bus is busy. + */ +status_t I2C_MasterStart(I2C_Type *base, uint8_t address, i2c_direction_t direction); + +/*! + * @brief Sends a STOP signal on the I2C bus. + * + * @retval kStatus_Success Successfully send the stop signal. + * @retval kStatus_I2C_Timeout Send stop signal failed, timeout. + */ +status_t I2C_MasterStop(I2C_Type *base); + +/*! + * @brief Sends a REPEATED START on the I2C bus. + * + * @param base I2C peripheral base pointer + * @param address 7-bit slave device address. + * @param direction Master transfer directions(transmit/receive). + * @retval kStatus_Success Successfully send the start signal. + * @retval kStatus_I2C_Busy Current bus is busy but not occupied by current I2C master. + */ +static inline status_t I2C_MasterRepeatedStart(I2C_Type *base, uint8_t address, i2c_direction_t direction) +{ + return I2C_MasterStart(base, address, direction); +} + +/*! + * @brief Performs a polling send transfer on the I2C bus. + * + * Sends up to @a txSize number of bytes to the previously addressed slave device. The slave may + * reply with a NAK to any byte in order to terminate the transfer early. If this happens, this + * function returns #kStatus_I2C_Nak. + * + * @param base The I2C peripheral base address. + * @param txBuff The pointer to the data to be transferred. + * @param txSize The length in bytes of the data to be transferred. + * @param flags Transfer control flag to control special behavior like suppressing start or stop, for normal transfers + * use kI2C_TransferDefaultFlag + * @retval kStatus_Success Data was sent successfully. + * @retval #kStatus_I2C_Busy Another master is currently utilizing the bus. + * @retval #kStatus_I2C_Nak The slave device sent a NAK in response to a byte. + * @retval #kStatus_I2C_ArbitrationLost Arbitration lost error. + */ +status_t I2C_MasterWriteBlocking(I2C_Type *base, const void *txBuff, size_t txSize, uint32_t flags); + +/*! + * @brief Performs a polling receive transfer on the I2C bus. + * + * @param base The I2C peripheral base address. + * @param rxBuff The pointer to the data to be transferred. + * @param rxSize The length in bytes of the data to be transferred. + * @param flags Transfer control flag to control special behavior like suppressing start or stop, for normal transfers + * use kI2C_TransferDefaultFlag + * @retval kStatus_Success Data was received successfully. + * @retval #kStatus_I2C_Busy Another master is currently utilizing the bus. + * @retval #kStatus_I2C_Nak The slave device sent a NAK in response to a byte. + * @retval #kStatus_I2C_ArbitrationLost Arbitration lost error. + */ +status_t I2C_MasterReadBlocking(I2C_Type *base, void *rxBuff, size_t rxSize, uint32_t flags); + +/*! + * @brief Performs a master polling transfer on the I2C bus. + * + * @note The API does not return until the transfer succeeds or fails due + * to arbitration lost or receiving a NAK. + * + * @param base I2C peripheral base address. + * @param xfer Pointer to the transfer structure. + * @retval kStatus_Success Successfully complete the data transmission. + * @retval kStatus_I2C_Busy Previous transmission still not finished. + * @retval kStatus_I2C_Timeout Transfer error, wait signal timeout. + * @retval kStatus_I2C_ArbitrationLost Transfer error, arbitration lost. + * @retval kStataus_I2C_Nak Transfer error, receive NAK during transfer. + * @retval kStataus_I2C_Addr_Nak Transfer error, receive NAK during addressing. + */ +status_t I2C_MasterTransferBlocking(I2C_Type *base, i2c_master_transfer_t *xfer); + +/*@}*/ + +/*! @name Non-blocking */ +/*@{*/ + +/*! + * @brief Creates a new handle for the I2C master non-blocking APIs. + * + * The creation of a handle is for use with the non-blocking APIs. Once a handle + * is created, there is not a corresponding destroy handle. If the user wants to + * terminate a transfer, the I2C_MasterTransferAbort() API shall be called. + * + * @param base The I2C peripheral base address. + * @param[out] handle Pointer to the I2C master driver handle. + * @param callback User provided pointer to the asynchronous callback function. + * @param userData User provided pointer to the application callback data. + */ +void I2C_MasterTransferCreateHandle(I2C_Type *base, + i2c_master_handle_t *handle, + i2c_master_transfer_callback_t callback, + void *userData); + +/*! + * @brief Performs a non-blocking transaction on the I2C bus. + * + * @param base The I2C peripheral base address. + * @param handle Pointer to the I2C master driver handle. + * @param xfer The pointer to the transfer descriptor. + * @retval kStatus_Success The transaction was started successfully. + * @retval #kStatus_I2C_Busy Either another master is currently utilizing the bus, or a non-blocking + * transaction is already in progress. + */ +status_t I2C_MasterTransferNonBlocking(I2C_Type *base, i2c_master_handle_t *handle, i2c_master_transfer_t *xfer); + +/*! + * @brief Returns number of bytes transferred so far. + * @param base The I2C peripheral base address. + * @param handle Pointer to the I2C master driver handle. + * @param[out] count Number of bytes transferred so far by the non-blocking transaction. + * @retval kStatus_Success + * @retval #kStatus_I2C_Busy + */ +status_t I2C_MasterTransferGetCount(I2C_Type *base, i2c_master_handle_t *handle, size_t *count); + +/*! + * @brief Terminates a non-blocking I2C master transmission early. + * + * @note It is not safe to call this function from an IRQ handler that has a higher priority than the + * I2C peripheral's IRQ priority. + * + * @param base The I2C peripheral base address. + * @param handle Pointer to the I2C master driver handle. + * @retval kStatus_Success A transaction was successfully aborted. + * @retval #kStatus_I2C_Timeout Timeout during polling for flags. + */ +status_t I2C_MasterTransferAbort(I2C_Type *base, i2c_master_handle_t *handle); + +/*@}*/ + +/*! @name IRQ handler */ +/*@{*/ + +/*! + * @brief Reusable routine to handle master interrupts. + * @note This function does not need to be called unless you are reimplementing the + * nonblocking API's interrupt handler routines to add special functionality. + * @param base The I2C peripheral base address. + * @param handle Pointer to the I2C master driver handle. + */ +void I2C_MasterTransferHandleIRQ(I2C_Type *base, i2c_master_handle_t *handle); + +/*@}*/ + +/*! @} */ /* end of i2c_master_driver */ + +/*! + * @addtogroup i2c_slave_driver + * @{ + */ + +/*! @name Slave initialization and deinitialization */ +/*@{*/ + +/*! + * @brief Provides a default configuration for the I2C slave peripheral. + * + * This function provides the following default configuration for the I2C slave peripheral: + * @code + * slaveConfig->enableSlave = true; + * slaveConfig->address0.disable = false; + * slaveConfig->address0.address = 0u; + * slaveConfig->address1.disable = true; + * slaveConfig->address2.disable = true; + * slaveConfig->address3.disable = true; + * slaveConfig->busSpeed = kI2C_SlaveStandardMode; + * @endcode + * + * After calling this function, override any settings to customize the configuration, + * prior to initializing the master driver with I2C_SlaveInit(). Be sure to override at least the @a + * address0.address member of the configuration structure with the desired slave address. + * + * @param[out] slaveConfig User provided configuration structure that is set to default values. Refer to + * #i2c_slave_config_t. + */ +void I2C_SlaveGetDefaultConfig(i2c_slave_config_t *slaveConfig); + +/*! + * @brief Initializes the I2C slave peripheral. + * + * This function enables the peripheral clock and initializes the I2C slave peripheral as described by the user + * provided configuration. + * + * @param base The I2C peripheral base address. + * @param slaveConfig User provided peripheral configuration. Use I2C_SlaveGetDefaultConfig() to get a set of defaults + * that you can override. + * @param srcClock_Hz Frequency in Hertz of the I2C functional clock. Used to calculate CLKDIV value to provide + * enough + * data setup time for master when slave stretches the clock. + */ +status_t I2C_SlaveInit(I2C_Type *base, const i2c_slave_config_t *slaveConfig, uint32_t srcClock_Hz); + +/*! + * @brief Configures Slave Address n register. + * + * This function writes new value to Slave Address register. + * + * @param base The I2C peripheral base address. + * @param addressRegister The module supports multiple address registers. The parameter determines which one shall be + * changed. + * @param address The slave address to be stored to the address register for matching. + * @param addressDisable Disable matching of the specified address register. + */ +void I2C_SlaveSetAddress(I2C_Type *base, + i2c_slave_address_register_t addressRegister, + uint8_t address, + bool addressDisable); + +/*! + * @brief Deinitializes the I2C slave peripheral. + * + * This function disables the I2C slave peripheral and gates the clock. It also performs a software + * reset to restore the peripheral to reset conditions. + * + * @param base The I2C peripheral base address. + */ +void I2C_SlaveDeinit(I2C_Type *base); + +/*! + * @brief Enables or disables the I2C module as slave. + * + * @param base The I2C peripheral base address. + * @param enable True to enable or flase to disable. + */ +static inline void I2C_SlaveEnable(I2C_Type *base, bool enable) +{ + /* Set or clear the SLVEN bit in the CFG register. */ + base->CFG = I2C_CFG_SLVEN(enable); +} + +/*@}*/ /* end of Slave initialization and deinitialization */ + +/*! @name Slave status */ +/*@{*/ + +/*! + * @brief Clears the I2C status flag state. + * + * The following status register flags can be cleared: + * - slave deselected flag + * + * Attempts to clear other flags has no effect. + * + * @param base The I2C peripheral base address. + * @param statusMask A bitmask of status flags that are to be cleared. The mask is composed of + * _i2c_slave_flags enumerators OR'd together. You may pass the result of a previous call to + * I2C_SlaveGetStatusFlags(). + * @see _i2c_slave_flags. + */ +static inline void I2C_SlaveClearStatusFlags(I2C_Type *base, uint32_t statusMask) +{ + /* Allow clearing just slave status flags */ + base->STAT = statusMask & I2C_STAT_SLVDESEL_MASK; +} + +/*@}*/ /* end of Slave status */ + +/*! @name Slave bus operations */ +/*@{*/ + +/*! + * @brief Performs a polling send transfer on the I2C bus. + * + * The function executes blocking address phase and blocking data phase. + * + * @param base The I2C peripheral base address. + * @param txBuff The pointer to the data to be transferred. + * @param txSize The length in bytes of the data to be transferred. + * @return kStatus_Success Data has been sent. + * @return kStatus_Fail Unexpected slave state (master data write while master read from slave is expected). + */ +status_t I2C_SlaveWriteBlocking(I2C_Type *base, const uint8_t *txBuff, size_t txSize); + +/*! + * @brief Performs a polling receive transfer on the I2C bus. + * + * The function executes blocking address phase and blocking data phase. + * + * @param base The I2C peripheral base address. + * @param rxBuff The pointer to the data to be transferred. + * @param rxSize The length in bytes of the data to be transferred. + * @return kStatus_Success Data has been received. + * @return kStatus_Fail Unexpected slave state (master data read while master write to slave is expected). + */ +status_t I2C_SlaveReadBlocking(I2C_Type *base, uint8_t *rxBuff, size_t rxSize); + +/*@}*/ /* end of Slave bus operations */ + +/*! @name Slave non-blocking */ +/*@{*/ + +/*! + * @brief Creates a new handle for the I2C slave non-blocking APIs. + * + * The creation of a handle is for use with the non-blocking APIs. Once a handle + * is created, there is not a corresponding destroy handle. If the user wants to + * terminate a transfer, the I2C_SlaveTransferAbort() API shall be called. + * + * @param base The I2C peripheral base address. + * @param[out] handle Pointer to the I2C slave driver handle. + * @param callback User provided pointer to the asynchronous callback function. + * @param userData User provided pointer to the application callback data. + */ +void I2C_SlaveTransferCreateHandle(I2C_Type *base, + i2c_slave_handle_t *handle, + i2c_slave_transfer_callback_t callback, + void *userData); + +/*! + * @brief Starts accepting slave transfers. + * + * Call this API after calling I2C_SlaveInit() and I2C_SlaveTransferCreateHandle() to start processing + * transactions driven by an I2C master. The slave monitors the I2C bus and pass events to the + * callback that was passed into the call to I2C_SlaveTransferCreateHandle(). The callback is always invoked + * from the interrupt context. + * + * If no slave Tx transfer is busy, a master read from slave request invokes #kI2C_SlaveTransmitEvent callback. + * If no slave Rx transfer is busy, a master write to slave request invokes #kI2C_SlaveReceiveEvent callback. + * + * The set of events received by the callback is customizable. To do so, set the @a eventMask parameter to + * the OR'd combination of #i2c_slave_transfer_event_t enumerators for the events you wish to receive. + * The #kI2C_SlaveTransmitEvent and #kI2C_SlaveReceiveEvent events are always enabled and do not need + * to be included in the mask. Alternatively, you can pass 0 to get a default set of only the transmit and + * receive events that are always enabled. In addition, the #kI2C_SlaveAllEvents constant is provided as + * a convenient way to enable all events. + * + * @param base The I2C peripheral base address. + * @param handle Pointer to i2c_slave_handle_t structure which stores the transfer state. + * @param eventMask Bit mask formed by OR'ing together #i2c_slave_transfer_event_t enumerators to specify + * which events to send to the callback. Other accepted values are 0 to get a default set of + * only the transmit and receive events, and #kI2C_SlaveAllEvents to enable all events. + * + * @retval kStatus_Success Slave transfers were successfully started. + * @retval #kStatus_I2C_Busy Slave transfers have already been started on this handle. + */ +status_t I2C_SlaveTransferNonBlocking(I2C_Type *base, i2c_slave_handle_t *handle, uint32_t eventMask); + +/*! + * @brief Starts accepting master read from slave requests. + * + * The function can be called in response to #kI2C_SlaveTransmitEvent callback to start a new slave Tx transfer + * from within the transfer callback. + * + * The set of events received by the callback is customizable. To do so, set the @a eventMask parameter to + * the OR'd combination of #i2c_slave_transfer_event_t enumerators for the events you wish to receive. + * The #kI2C_SlaveTransmitEvent and #kI2C_SlaveReceiveEvent events are always enabled and do not need + * to be included in the mask. Alternatively, you can pass 0 to get a default set of only the transmit and + * receive events that are always enabled. In addition, the #kI2C_SlaveAllEvents constant is provided as + * a convenient way to enable all events. + * + * @param base The I2C peripheral base address. + * @param transfer Pointer to #i2c_slave_transfer_t structure. + * @param txData Pointer to data to send to master. + * @param txSize Size of txData in bytes. + * @param eventMask Bit mask formed by OR'ing together #i2c_slave_transfer_event_t enumerators to specify + * which events to send to the callback. Other accepted values are 0 to get a default set of + * only the transmit and receive events, and #kI2C_SlaveAllEvents to enable all events. + * + * @retval kStatus_Success Slave transfers were successfully started. + * @retval #kStatus_I2C_Busy Slave transfers have already been started on this handle. + */ +status_t I2C_SlaveSetSendBuffer( + I2C_Type *base, volatile i2c_slave_transfer_t *transfer, const void *txData, size_t txSize, uint32_t eventMask); + +/*! + * @brief Starts accepting master write to slave requests. + * + * The function can be called in response to #kI2C_SlaveReceiveEvent callback to start a new slave Rx transfer + * from within the transfer callback. + * + * The set of events received by the callback is customizable. To do so, set the @a eventMask parameter to + * the OR'd combination of #i2c_slave_transfer_event_t enumerators for the events you wish to receive. + * The #kI2C_SlaveTransmitEvent and #kI2C_SlaveReceiveEvent events are always enabled and do not need + * to be included in the mask. Alternatively, you can pass 0 to get a default set of only the transmit and + * receive events that are always enabled. In addition, the #kI2C_SlaveAllEvents constant is provided as + * a convenient way to enable all events. + * + * @param base The I2C peripheral base address. + * @param transfer Pointer to #i2c_slave_transfer_t structure. + * @param rxData Pointer to data to store data from master. + * @param rxSize Size of rxData in bytes. + * @param eventMask Bit mask formed by OR'ing together #i2c_slave_transfer_event_t enumerators to specify + * which events to send to the callback. Other accepted values are 0 to get a default set of + * only the transmit and receive events, and #kI2C_SlaveAllEvents to enable all events. + * + * @retval kStatus_Success Slave transfers were successfully started. + * @retval #kStatus_I2C_Busy Slave transfers have already been started on this handle. + */ +status_t I2C_SlaveSetReceiveBuffer( + I2C_Type *base, volatile i2c_slave_transfer_t *transfer, void *rxData, size_t rxSize, uint32_t eventMask); + +/*! + * @brief Returns the slave address sent by the I2C master. + * + * This function should only be called from the address match event callback #kI2C_SlaveAddressMatchEvent. + * + * @param base The I2C peripheral base address. + * @param transfer The I2C slave transfer. + * @return The 8-bit address matched by the I2C slave. Bit 0 contains the R/w direction bit, and + * the 7-bit slave address is in the upper 7 bits. + */ +static inline uint32_t I2C_SlaveGetReceivedAddress(I2C_Type *base, volatile i2c_slave_transfer_t *transfer) +{ + return transfer->receivedAddress; +} + +/*! + * @brief Aborts the slave non-blocking transfers. + * @note This API could be called at any time to stop slave for handling the bus events. + * @param base The I2C peripheral base address. + * @param handle Pointer to i2c_slave_handle_t structure which stores the transfer state. + * @retval kStatus_Success + * @retval #kStatus_I2C_Idle + */ +void I2C_SlaveTransferAbort(I2C_Type *base, i2c_slave_handle_t *handle); + +/*! + * @brief Gets the slave transfer remaining bytes during a interrupt non-blocking transfer. + * + * @param base I2C base pointer. + * @param handle pointer to i2c_slave_handle_t structure. + * @param count Number of bytes transferred so far by the non-blocking transaction. + * @retval kStatus_InvalidArgument count is Invalid. + * @retval kStatus_Success Successfully return the count. + */ +status_t I2C_SlaveTransferGetCount(I2C_Type *base, i2c_slave_handle_t *handle, size_t *count); + +/*@}*/ /* end of Slave non-blocking */ + +/*! @name Slave IRQ handler */ +/*@{*/ + +/*! + * @brief Reusable routine to handle slave interrupts. + * @note This function does not need to be called unless you are reimplementing the + * non blocking API's interrupt handler routines to add special functionality. + * @param base The I2C peripheral base address. + * @param handle Pointer to i2c_slave_handle_t structure which stores the transfer state. + */ +void I2C_SlaveTransferHandleIRQ(I2C_Type *base, i2c_slave_handle_t *handle); + +/*@}*/ /* end of Slave IRQ handler */ + +/*! @} */ /* end of i2c_slave_driver */ + +#if defined(__cplusplus) +} +#endif + +#endif /* _FSL_I2C_H_ */ diff --git a/drivers/fsl_iocon.h b/drivers/fsl_iocon.h new file mode 100644 index 0000000..3e95508 --- /dev/null +++ b/drivers/fsl_iocon.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _FSL_IOCON_H_ +#define _FSL_IOCON_H_ + +#include "fsl_common.h" + +/*! + * @addtogroup lpc_iocon + * @{ + */ + +/*! @file */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.lpc_iocon" +#endif + +/*! @name Driver version */ +/*@{*/ +/*! @brief IOCON driver version. */ +#define FSL_IOCON_DRIVER_VERSION (MAKE_VERSION(2, 2, 0)) +/*@}*/ + +/** + * @brief Array of IOCON pin definitions passed to IOCON_SetPinMuxing() must be in this format + */ +typedef struct _iocon_group +{ + uint8_t port; /* Pin port */ + uint8_t pin; /* Pin number */ + uint8_t ionumber; /* IO number */ + uint16_t modefunc; /* Function and mode */ +} iocon_group_t; + +/** + * @brief IOCON function and mode selection definitions + * @note See the User Manual for specific modes and functions supported by the various pins. + */ +#define IOCON_FUNC0 0x0 /*!< Selects pin function 0 */ +#define IOCON_FUNC1 0x1 /*!< Selects pin function 1 */ +#define IOCON_FUNC2 0x2 /*!< Selects pin function 2 */ +#define IOCON_FUNC3 0x3 /*!< Selects pin function 3 */ +#define IOCON_FUNC4 0x4 /*!< Selects pin function 4 */ +#define IOCON_FUNC5 0x5 /*!< Selects pin function 5 */ +#define IOCON_FUNC6 0x6 /*!< Selects pin function 6 */ +#define IOCON_FUNC7 0x7 /*!< Selects pin function 7 */ +#if defined(FSL_FEATURE_IOCON_FUNC_FIELD_WIDTH) && (FSL_FEATURE_IOCON_FUNC_FIELD_WIDTH == 4) +#define IOCON_FUNC8 0x8 /*!< Selects pin function 8 */ +#define IOCON_FUNC9 0x9 /*!< Selects pin function 9 */ +#define IOCON_FUNC10 0xA /*!< Selects pin function 10 */ +#define IOCON_FUNC11 0xB /*!< Selects pin function 11 */ +#define IOCON_FUNC12 0xC /*!< Selects pin function 12 */ +#define IOCON_FUNC13 0xD /*!< Selects pin function 13 */ +#define IOCON_FUNC14 0xE /*!< Selects pin function 14 */ +#define IOCON_FUNC15 0xF /*!< Selects pin function 15 */ +#endif /* FSL_FEATURE_IOCON_FUNC_FIELD_WIDTH */ + +#if defined(IOCON_PIO_MODE_SHIFT) +#define IOCON_MODE_INACT (0x0 << IOCON_PIO_MODE_SHIFT) /*!< No addition pin function */ +#define IOCON_MODE_PULLDOWN (0x1 << IOCON_PIO_MODE_SHIFT) /*!< Selects pull-down function */ +#define IOCON_MODE_PULLUP (0x2 << IOCON_PIO_MODE_SHIFT) /*!< Selects pull-up function */ +#define IOCON_MODE_REPEATER (0x3 << IOCON_PIO_MODE_SHIFT) /*!< Selects pin repeater function */ +#endif + +#if defined(IOCON_PIO_I2CSLEW_SHIFT) +#define IOCON_GPIO_MODE (0x1 << IOCON_PIO_I2CSLEW_SHIFT) /*!< GPIO Mode */ +#define IOCON_I2C_MODE (0x0 << IOCON_PIO_I2CSLEW_SHIFT) /*!< I2C Slew Rate Control */ +#define IOCON_I2C_SLEW IOCON_I2C_MODE /*!< Deprecated name for #IOCON_I2C_MODE */ +#endif + +#if defined(IOCON_PIO_EGP_SHIFT) +#define IOCON_GPIO_MODE (0x1 << IOCON_PIO_EGP_SHIFT) /*!< GPIO Mode */ +#define IOCON_I2C_MODE (0x0 << IOCON_PIO_EGP_SHIFT) /*!< I2C Slew Rate Control */ +#define IOCON_I2C_SLEW IOCON_I2C_MODE /*!< Deprecated name for #IOCON_I2C_MODE */ +#endif + +#if defined(IOCON_PIO_SLEW_SHIFT) +#define IOCON_SLEW_STANDARD (0x0 << IOCON_PIO_SLEW_SHIFT) /*!< Driver Slew Rate Control */ +#define IOCON_SLEW_FAST (0x1 << IOCON_PIO_SLEW_SHIFT) /*!< Driver Slew Rate Control */ +#endif + +#if defined(IOCON_PIO_INVERT_SHIFT) +#define IOCON_INV_EN (0x1 << IOCON_PIO_INVERT_SHIFT) /*!< Enables invert function on input */ +#endif + +#if defined(IOCON_PIO_DIGIMODE_SHIFT) +#define IOCON_ANALOG_EN (0x0 << IOCON_PIO_DIGIMODE_SHIFT) /*!< Enables analog function by setting 0 to bit 7 */ +#define IOCON_DIGITAL_EN \ + (0x1 << IOCON_PIO_DIGIMODE_SHIFT) /*!< Enables digital function by setting 1 to bit 7(default) */ +#endif + +#if defined(IOCON_PIO_FILTEROFF_SHIFT) +#define IOCON_INPFILT_OFF (0x1 << IOCON_PIO_FILTEROFF_SHIFT) /*!< Input filter Off for GPIO pins */ +#define IOCON_INPFILT_ON (0x0 << IOCON_PIO_FILTEROFF_SHIFT) /*!< Input filter On for GPIO pins */ +#endif + +#if defined(IOCON_PIO_I2CDRIVE_SHIFT) +#define IOCON_I2C_LOWDRIVER (0x0 << IOCON_PIO_I2CDRIVE_SHIFT) /*!< Low drive, Output drive sink is 4 mA */ +#define IOCON_I2C_HIGHDRIVER (0x1 << IOCON_PIO_I2CDRIVE_SHIFT) /*!< High drive, Output drive sink is 20 mA */ +#endif + +#if defined(IOCON_PIO_OD_SHIFT) +#define IOCON_OPENDRAIN_EN (0x1 << IOCON_PIO_OD_SHIFT) /*!< Enables open-drain function */ +#endif + +#if defined(IOCON_PIO_I2CFILTER_SHIFT) +#define IOCON_I2CFILTER_OFF (0x1 << IOCON_PIO_I2CFILTER_SHIFT) /*!< I2C 50 ns glitch filter enabled */ +#define IOCON_I2CFILTER_ON (0x0 << IOCON_PIO_I2CFILTER_SHIFT) /*!< I2C 50 ns glitch filter not enabled, */ +#endif + +#if defined(IOCON_PIO_ASW_SHIFT) +#define IOCON_AWS_EN (0x1 << IOCON_PIO_ASW_SHIFT) /*!< Enables analog switch function */ +#endif + +#if defined(IOCON_PIO_SSEL_SHIFT) +#define IOCON_SSEL_3V3 (0x0 << IOCON_PIO_SSEL_SHIFT) /*!< 3V3 signaling in I2C mode */ +#define IOCON_SSEL_1V8 (0x1 << IOCON_PIO_SSEL_SHIFT) /*!< 1V8 signaling in I2C mode */ +#endif + +#if defined(IOCON_PIO_ECS_SHIFT) +#define IOCON_ECS_OFF (0x0 << IOCON_PIO_ECS_SHIFT) /*!< IO is an open drain cell */ +#define IOCON_ECS_ON (0x1 << IOCON_PIO_ECS_SHIFT) /*!< Pull-up resistor is connected */ +#endif + +#if defined(IOCON_PIO_S_MODE_SHIFT) +#define IOCON_S_MODE_0CLK (0x0 << IOCON_PIO_S_MODE_SHIFT) /*!< Bypass input filter */ +#define IOCON_S_MODE_1CLK \ + (0x1 << IOCON_PIO_S_MODE_SHIFT) /*!< Input pulses shorter than 1 filter clock are rejected \ \ \ \ \ + */ +#define IOCON_S_MODE_2CLK \ + (0x2 << IOCON_PIO_S_MODE_SHIFT) /*!< Input pulses shorter than 2 filter clock2 are rejected \ \ \ \ \ + */ +#define IOCON_S_MODE_3CLK \ + (0x3 << IOCON_PIO_S_MODE_SHIFT) /*!< Input pulses shorter than 3 filter clock2 are rejected \ \ \ \ \ + */ +#define IOCON_S_MODE(clks) ((clks) << IOCON_PIO_S_MODE_SHIFT) /*!< Select clocks for digital input filter mode */ +#endif + +#if defined(IOCON_PIO_CLK_DIV_SHIFT) +#define IOCON_CLKDIV(div) \ + ((div) \ + << IOCON_PIO_CLK_DIV_SHIFT) /*!< Select peripheral clock divider for input filter sampling clock, 2^n, n=0-6 */ +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#if (defined(FSL_FEATURE_IOCON_ONE_DIMENSION) && (FSL_FEATURE_IOCON_ONE_DIMENSION == 1)) +/** + * @brief Sets I/O Control pin mux + * @param base : The base of IOCON peripheral on the chip + * @param ionumber : GPIO number to mux + * @param modefunc : OR'ed values of type IOCON_* + * @return Nothing + */ +__STATIC_INLINE void IOCON_PinMuxSet(IOCON_Type *base, uint8_t ionumber, uint32_t modefunc) +{ + base->PIO[ionumber] = modefunc; +} +#else +/** + * @brief Sets I/O Control pin mux + * @param base : The base of IOCON peripheral on the chip + * @param port : GPIO port to mux + * @param pin : GPIO pin to mux + * @param modefunc : OR'ed values of type IOCON_* + * @return Nothing + */ +__STATIC_INLINE void IOCON_PinMuxSet(IOCON_Type *base, uint8_t port, uint8_t pin, uint32_t modefunc) +{ + base->PIO[port][pin] = modefunc; +} +#endif + +/** + * @brief Set all I/O Control pin muxing + * @param base : The base of IOCON peripheral on the chip + * @param pinArray : Pointer to array of pin mux selections + * @param arrayLength : Number of entries in pinArray + * @return Nothing + */ +__STATIC_INLINE void IOCON_SetPinMuxing(IOCON_Type *base, const iocon_group_t *pinArray, uint32_t arrayLength) +{ + uint32_t i; + + for (i = 0; i < arrayLength; i++) + { +#if (defined(FSL_FEATURE_IOCON_ONE_DIMENSION) && (FSL_FEATURE_IOCON_ONE_DIMENSION == 1)) + IOCON_PinMuxSet(base, pinArray[i].ionumber, pinArray[i].modefunc); +#else + IOCON_PinMuxSet(base, pinArray[i].port, pinArray[i].pin, pinArray[i].modefunc); +#endif /* FSL_FEATURE_IOCON_ONE_DIMENSION */ + } +} + +/* @} */ + +#if defined(__cplusplus) +} +#endif + +#endif /* _FSL_IOCON_H_ */ diff --git a/drivers/fsl_power.c b/drivers/fsl_power.c new file mode 100644 index 0000000..24e3f83 --- /dev/null +++ b/drivers/fsl_power.c @@ -0,0 +1,1985 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016, NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "fsl_common.h" +#include "fsl_power.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.power" +#endif + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/******************************************************************************* + * Definitions + ******************************************************************************/ +/** @brief Low Power main structure */ +typedef enum +{ + VD_AON = 0x0, /*!< Digital Always On power domain */ + VD_MEM = 0x1, /*!< Memories (SRAM) power domain */ + VD_DCDC = 0x2, /*!< Core logic power domain */ + VD_DEEPSLEEP = 0x3 /*!< Core logic power domain */ +} LPC_POWER_DOMAIN_T; + +/** + * @brief LDO_FLASH_NV & LDO_USB voltage settings + */ +typedef enum _v_flashnv +{ + V_LDOFLASHNV_1P650 = 0, /*!< 0.95 V */ + V_LDOFLASHNV_1P700 = 1, /*!< 0.975 V */ + V_LDOFLASHNV_1P750 = 2, /*!< 1 V */ + V_LDOFLASHNV_0P800 = 3, /*!< 1.025 V */ + V_LDOFLASHNV_1P850 = 4, /*!< 1.050 V */ + V_LDOFLASHNV_1P900 = 5, /*!< 1.075 V */ + V_LDOFLASHNV_1P950 = 6, /*!< 1.1 V */ + V_LDOFLASHNV_2P000 = 7 /*!< 1.125 V */ +} v_flashnv_t; + +/** @brief Low Power main structure */ +typedef struct +{ /* */ + __IO uint32_t CFG; /*!< Low Power Mode Configuration, and miscallenous options */ + __IO uint32_t PDCTRL0; /*!< Power Down control : controls power of various modules + in the different Low power modes, including ROM */ + __IO uint32_t SRAMRETCTRL; /*!< Power Down control : controls power SRAM instances + in the different Low power modes */ + __IO uint32_t CPURETCTRL; /*!< CPU0 retention control : controls CPU retention parameters in POWER DOWN modes */ + __IO uint64_t VOLTAGE; /*!< Voltage control in Low Power Modes */ + __IO uint64_t WAKEUPSRC; /*!< Wake up sources control for sleepcon */ + __IO uint64_t WAKEUPINT; /*!< Wake up sources control for ARM */ + __IO uint32_t HWWAKE; /*!< Interrupt that can postpone power down modes + in case an interrupt is pending when the processor request deepsleep */ + __IO uint32_t WAKEUPIOSRC; /*!< Wake up I/O sources in DEEP POWER DOWN mode */ + __IO uint32_t TIMERCFG; /*!< Wake up timers configuration */ + __IO uint32_t TIMERCOUNT; /*!< Wake up Timer count*/ + __IO uint32_t POWERCYCLE; /*!< Cancels entry in Low Power mode if set with 0xDEADABBA (might be used by some + interrupt handlers)*/ +} LPC_LOWPOWER_T; + +/* */ +#define LOWPOWER_POWERCYCLE_CANCELLED 0xDEADABBAUL /*!< */ + +/** + * @brief SRAM Low Power Modes + */ +#define LOWPOWER_SRAM_LPMODE_MASK (0xFUL) +#define LOWPOWER_SRAM_LPMODE_ACTIVE (0x6UL) /*!< SRAM functional mode */ +#define LOWPOWER_SRAM_LPMODE_SLEEP (0xFUL) /*!< SRAM Sleep mode (Data retention, fast wake up) */ +#define LOWPOWER_SRAM_LPMODE_DEEPSLEEP (0x8UL) /*!< SRAM Deep Sleep mode (Data retention, slow wake up) */ +#define LOWPOWER_SRAM_LPMODE_SHUTDOWN (0x9UL) /*!< SRAM Shut Down mode (no data retention) */ +#define LOWPOWER_SRAM_LPMODE_POWERUP (0xAUL) /*!< SRAM is powering up */ + +/** + * @brief Wake up timers configuration in Low Power Modes + */ +#define LOWPOWER_TIMERCFG_CTRL_INDEX 0 +#define LOWPOWER_TIMERCFG_CTRL_MASK (0x1UL << LOWPOWER_TIMERCFG_CTRL_INDEX) +#define LOWPOWER_TIMERCFG_TIMER_INDEX 1 +#define LOWPOWER_TIMERCFG_TIMER_MASK (0x7UL << LOWPOWER_TIMERCFG_TIMER_INDEX) +#define LOWPOWER_TIMERCFG_OSC32K_INDEX 4 +#define LOWPOWER_TIMERCFG_OSC32K_MASK (0x1UL << LOWPOWER_TIMERCFG_OSC32K_INDEX) + +#define LOWPOWER_TIMERCFG_CTRL_DISABLE 0 /*!< Wake Timer Disable */ +#define LOWPOWER_TIMERCFG_CTRL_ENABLE 1 /*!< Wake Timer Enable */ + +/** + * @brief Primary Wake up timers configuration in Low Power Modes + */ +#define LOWPOWER_TIMERCFG_TIMER_RTC1KHZ 0 /*!< 1 KHz Real Time Counter (RTC) used as wake up source */ +#define LOWPOWER_TIMERCFG_TIMER_RTC1HZ 1 /*!< 1 Hz Real Time Counter (RTC) used as wake up source */ +#define LOWPOWER_TIMERCFG_TIMER_OSTIMER 2 /*!< OS Event Timer used as wake up source */ + +#define LOWPOWER_TIMERCFG_OSC32K_FRO32KHZ 0 /*!< Wake up Timers uses FRO 32 KHz as clock source */ +#define LOWPOWER_TIMERCFG_OSC32K_XTAL32KHZ 1 /*!< Wake up Timers uses Chrystal 32 KHz as clock source */ + +//! @brief Interface for lowpower functions +typedef struct LowpowerDriverInterface +{ + void (*power_cycle_cpu_and_flash)(void); + void (*set_lowpower_mode)(LPC_LOWPOWER_T *p_lowpower_cfg); +} lowpower_driver_interface_t; + +/**< DCDC Power Profiles */ +typedef enum +{ + DCDC_POWER_PROFILE_LOW, /**< LOW (for CPU frequencies below DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ) */ + DCDC_POWER_PROFILE_MEDIUM, /**< MEDIUM (for CPU frequencies between DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ and + DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ) */ + DCDC_POWER_PROFILE_HIGH, /**< HIGH (for CPU frequencies between DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ and + DCDC_POWER_PROFILE_HIGH_MAX_FREQ_HZ) */ +} lowpower_dcdc_power_profile_enum; + +/**< Manufacturing Process Corners */ +typedef enum +{ + PROCESS_CORNER_SSS, /**< Slow Corner Process */ + PROCESS_CORNER_NNN, /**< Nominal Corner Process */ + PROCESS_CORNER_FFF, /**< Fast Corner Process */ + PROCESS_CORNER_OTHERS, /**< SFN, SNF, NFS, Poly Res ... Corner Process */ +} lowpower_process_corner_enum; + +/** + * @brief DCDC voltage settings + */ +typedef enum _v_dcdc +{ + V_DCDC_0P950 = 0, /*!< 0.95 V */ + V_DCDC_0P975 = 1, /*!< 0.975 V */ + V_DCDC_1P000 = 2, /*!< 1 V */ + V_DCDC_1P025 = 3, /*!< 1.025 V */ + V_DCDC_1P050 = 4, /*!< 1.050 V */ + V_DCDC_1P075 = 5, /*!< 1.075 V */ + V_DCDC_1P100 = 6, /*!< 1.1 V */ + V_DCDC_1P125 = 7, /*!< 1.125 V */ + V_DCDC_1P150 = 8, /*!< 1.150 V */ + V_DCDC_1P175 = 9, /*!< 1.175 V */ + V_DCDC_1P200 = 10 /*!< 1.2 V */ +} v_dcdc_t; + +/** + * @brief Deep Sleep LDO voltage settings + */ +typedef enum _v_deepsleep +{ + V_DEEPSLEEP_0P900 = 0, /*!< 0.9 V */ + V_DEEPSLEEP_0P925 = 1, /*!< 0.925 V */ + V_DEEPSLEEP_0P950 = 2, /*!< 0.95 V */ + V_DEEPSLEEP_0P975 = 3, /*!< 0.975 V */ + V_DEEPSLEEP_1P000 = 4, /*!< 1.000 V */ + V_DEEPSLEEP_1P025 = 5, /*!< 1.025 V */ + V_DEEPSLEEP_1P050 = 6, /*!< 1.050 V */ + V_DEEPSLEEP_1P075 = 7 /*!< 1.075 V */ +} v_deepsleep_t; + +/** + * @brief Always On and Memories LDO voltage settings + */ +typedef enum _v_ao +{ + V_AO_0P700 = 1, /*!< 0.7 V */ + V_AO_0P725 = 2, /*!< 0.725 V */ + V_AO_0P750 = 3, /*!< 0.75 V */ + V_AO_0P775 = 4, /*!< 0.775 V */ + V_AO_0P800 = 5, /*!< 0.8 V */ + V_AO_0P825 = 6, /*!< 0.825 V */ + V_AO_0P850 = 7, /*!< 0.85 V */ + V_AO_0P875 = 8, /*!< 0.875 V */ + V_AO_0P900 = 9, /*!< 0.9 V */ + V_AO_0P960 = 10, /*!< 0.96 V */ + V_AO_0P970 = 11, /*!< 0.97 V */ + V_AO_0P980 = 12, /*!< 0.98 V */ + V_AO_0P990 = 13, /*!< 0.99 V */ + V_AO_1P000 = 14, /*!< 1 V */ + V_AO_1P010 = 15, /*!< 1.01 V */ + V_AO_1P020 = 16, /*!< 1.02 V */ + V_AO_1P030 = 17, /*!< 1.03 V */ + V_AO_1P040 = 18, /*!< 1.04 V */ + V_AO_1P050 = 19, /*!< 1.05 V */ + V_AO_1P060 = 20, /*!< 1.06 V */ + V_AO_1P070 = 21, /*!< 1.07 V */ + V_AO_1P080 = 22, /*!< 1.08 V */ + V_AO_1P090 = 23, /*!< 1.09 V */ + V_AO_1P100 = 24, /*!< 1.1 V */ + V_AO_1P110 = 25, /*!< 1.11 V */ + V_AO_1P120 = 26, /*!< 1.12 V */ + V_AO_1P130 = 27, /*!< 1.13 V */ + V_AO_1P140 = 28, /*!< 1.14 V */ + V_AO_1P150 = 29, /*!< 1.15 V */ + V_AO_1P160 = 30, /*!< 1.16 V */ + V_AO_1P220 = 31 /*!< 1.22 V */ +} v_ao_t; + +/* Low Power modes */ +#define LOWPOWER_CFG_LPMODE_INDEX 0 +#define LOWPOWER_CFG_LPMODE_MASK (0x3UL << LOWPOWER_CFG_LPMODE_INDEX) +#define LOWPOWER_CFG_SELCLOCK_INDEX 2 +#define LOWPOWER_CFG_SELCLOCK_MASK (0x1UL << LOWPOWER_CFG_SELCLOCK_INDEX) +#define LOWPOWER_CFG_SELMEMSUPPLY_INDEX 3 +#define LOWPOWER_CFG_SELMEMSUPPLY_MASK (0x1UL << LOWPOWER_CFG_SELMEMSUPPLY_INDEX) +#define LOWPOWER_CFG_MEMLOWPOWERMODE_INDEX 4 +#define LOWPOWER_CFG_MEMLOWPOWERMODE_MASK (0x1UL << LOWPOWER_CFG_MEMLOWPOWERMODE_INDEX) +#define LOWPOWER_CFG_LDODEEPSLEEPREF_INDEX 5 +#define LOWPOWER_CFG_LDODEEPSLEEPREF_MASK (0x1UL << LOWPOWER_CFG_LDODEEPSLEEPREF_INDEX) + +#define LOWPOWER_CFG_LPMODE_ACTIVE 0 /*!< ACTIVE mode */ +#define LOWPOWER_CFG_LPMODE_DEEPSLEEP 1 /*!< DEEP SLEEP mode */ +#define LOWPOWER_CFG_LPMODE_POWERDOWN 2 /*!< POWER DOWN mode */ +#define LOWPOWER_CFG_LPMODE_DEEPPOWERDOWN 3 /*!< DEEP POWER DOWN mode */ +#define LOWPOWER_CFG_LPMODE_SLEEP 4 /*!< SLEEP mode */ + +#define LOWPOWER_CFG_SELCLOCK_1MHZ 0 /*!< The 1 MHz clock is used during the configuration of the PMC */ +#define LOWPOWER_CFG_SELCLOCK_12MHZ \ + 1 /*!< The 12 MHz clock is used during the configuration of the PMC (to speed up PMC configuration process)*/ + +#define LOWPOWER_CFG_SELMEMSUPPLY_LDOMEM 0 /*!< In DEEP SLEEP power mode, the Memories are supplied by the LDO_MEM */ +#define LOWPOWER_CFG_SELMEMSUPPLY_LDODEEPSLEEP \ + 1 /*!< In DEEP SLEEP power mode, the Memories are supplied by the LDO_DEEP_SLEEP (or DCDC) */ + +#define LOWPOWER_CFG_MEMLOWPOWERMODE_SOURCEBIASING \ + 0 /*!< All SRAM instances use "Source Biasing" as low power mode technic (it is recommended to set LDO_MEM as high \ + as possible -- 1.1V typical -- during low power mode) */ +#define LOWPOWER_CFG_MEMLOWPOWERMODE_VOLTAGESCALING \ + 1 /*!< All SRAM instances use "Voltage Scaling" as low power mode technic (it is recommended to set LDO_MEM as low \ + as possible -- down to 0.7V -- during low power mode) */ + +/* CPU Retention Control*/ +#define LOWPOWER_CPURETCTRL_ENA_INDEX 0 +#define LOWPOWER_CPURETCTRL_ENA_MASK (0x1UL << LOWPOWER_CPURETCTRL_ENA_INDEX) +#define LOWPOWER_CPURETCTRL_MEMBASE_INDEX 1 +#define LOWPOWER_CPURETCTRL_MEMBASE_MASK (0x1FFFUL << LOWPOWER_CPURETCTRL_MEMBASE_INDEX) +#define LOWPOWER_CPURETCTRL_RETDATALENGTH_INDEX 14 +#define LOWPOWER_CPURETCTRL_RETDATALENGTH_MASK (0x3FFUL << LOWPOWER_CPURETCTRL_RETDATALENGTH_INDEX) + +/* Voltgae setting*/ +#define DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ \ + (72000000U) /* Maximum System Frequency allowed with DCDC Power Profile LOW */ +#define DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ \ + (100000000U) /* Maximum System Frequency allowed with DCDC Power Profile MEDIUM */ +#define DCDC_POWER_PROFILE_HIGH_MAX_FREQ_HZ \ + (150000000U) /* Maximum System Frequency allowed with DCDC Power Profile HIGH */ +#define PROCESS_NNN_AVG_HZ (19300000U) /* Average Ring OScillator value for Nominal (NNN) Manufacturing Process */ +#define PROCESS_NNN_STD_HZ \ + (400000U) /* Standard Deviation Ring OScillator value for Nominal (NNN) Manufacturing Process */ +#define PROCESS_NNN_LIMITS \ + (6U) /* Nominal (NNN) Manufacturing Process Ring Oscillator values limit (with respect to the Average value) */ +#define PROCESS_NNN_MIN_HZ \ + (PROCESS_NNN_AVG_HZ - \ + (PROCESS_NNN_LIMITS * \ + PROCESS_NNN_STD_HZ)) /* Minimum Ring OScillator value for Nominal (NNN) Manufacturing Process */ +#define PROCESS_NNN_MAX_HZ \ + (PROCESS_NNN_AVG_HZ + \ + (PROCESS_NNN_LIMITS * \ + PROCESS_NNN_STD_HZ)) /* Maximum Ring OScillator value for Nominal (NNN) Manufacturing Process */ +#define VOLTAGE_SSS_LOW_MV (1100U) /* Voltage Settings for : Process=SSS, DCDC Power Profile=LOW */ +#define VOLTAGE_SSS_MED_MV (1150U) /* Voltage Settings for : Process=SSS, DCDC Power Profile=MEDIUM */ +#define VOLTAGE_SSS_HIG_MV (1200U) /* Voltage Settings for : Process=SSS, DCDC Power Profile=HIGH */ +#define VOLTAGE_NNN_LOW_MV (1050U) /* Voltage Settings for : Process=NNN, DCDC Power Profile=LOW */ +#define VOLTAGE_NNN_MED_MV (1075U) /* Voltage Settings for : Process=NNN, DCDC Power Profile=MEDIUM */ +#define VOLTAGE_NNN_HIG_MV (1150U) /* Voltage Settings for : Process=NNN, DCDC Power Profile=HIGH */ +#define VOLTAGE_FFF_LOW_MV (1000U) /* Voltage Settings for : Process=FFF, DCDC Power Profile=LOW */ +#define VOLTAGE_FFF_MED_MV (1025U) /* Voltage Settings for : Process=FFF, DCDC Power Profile=MEDIUM */ +#define VOLTAGE_FFF_HIG_MV (1050U) /* Voltage Settings for : Process=FFF, DCDC Power Profile=HIGH */ + +/** + * @brief LDO Voltage control in Low Power Modes + */ +#define LOWPOWER_VOLTAGE_LDO_PMU_INDEX 0 +#define LOWPOWER_VOLTAGE_LDO_PMU_MASK (0x1FULL << LOWPOWER_VOLTAGE_LDO_PMU_INDEX) +#define LOWPOWER_VOLTAGE_LDO_MEM_INDEX 5 +#define LOWPOWER_VOLTAGE_LDO_MEM_MASK (0x1FULL << LOWPOWER_VOLTAGE_LDO_MEM_INDEX) +#define LOWPOWER_VOLTAGE_LDO_DEEP_SLEEP_INDEX 10 +#define LOWPOWER_VOLTAGE_LDO_DEEP_SLEEP_MASK (0x7ULL << LOWPOWER_VOLTAGE_LDO_DEEP_SLEEP_INDEX) +#define LOWPOWER_VOLTAGE_LDO_PMU_BOOST_INDEX 19 +#define LOWPOWER_VOLTAGE_LDO_PMU_BOOST_MASK (0x1FULL << LOWPOWER_VOLTAGE_LDO_PMU_BOOST_INDEX) +#define LOWPOWER_VOLTAGE_LDO_MEM_BOOST_INDEX 24 +#define LOWPOWER_VOLTAGE_LDO_MEM_BOOST_MASK (0x1FULL << LOWPOWER_VOLTAGE_LDO_MEM_BOOST_INDEX) +#define LOWPOWER_VOLTAGE_DCDC_INDEX 29 +#define LOWPOWER_VOLTAGE_DCDC_MASK (0xFULL << LOWPOWER_VOLTAGE_DCDC_INDEX) + +/*! @brief set and clear bit MACRO's. */ +#define U32_SET_BITS(P, B) ((*(uint32_t *)P) |= (B)) +#define U32_CLR_BITS(P, B) ((*(uint32_t *)P) &= ~(B)) +/* Return values from Config (N-2) page of flash */ +#define GET_16MXO_TRIM() (*(uint32_t *)(FLASH_NMPA_BASE + 0xC8U)) // (0x3FCC8) +#define GET_32KXO_TRIM() (*(uint32_t *)(FLASH_NMPA_BASE + 0xCCU)) // (0x3FCCC) + +#define CPU_RETENTION_RAMX_STORAGE_START_ADDR (0x04002000) + +#define XO_SLAVE_EN (1) +/******************************************************************************* + * Codes + ******************************************************************************/ + +/******************************************************************************* + * LOCAL FUNCTIONS PROTOTYPES + ******************************************************************************/ +static void lf_get_deepsleep_core_supply_cfg(uint32_t exclude_from_pd, uint32_t *dcdc_voltage); +static uint32_t lf_set_ldo_ao_ldo_mem_voltage(uint32_t p_lp_mode, uint32_t p_dcdc_voltage); +static uint32_t lf_wakeup_io_ctrl(uint32_t p_wakeup_io_ctrl); +static uint8_t CLOCK_u8OscCapConvert(uint8_t u8OscCap, uint8_t u8CapBankDiscontinuity); + +static void lowpower_set_dcdc_power_profile(lowpower_dcdc_power_profile_enum dcdc_power_profile); +static lowpower_process_corner_enum lowpower_get_part_process_corner(void); +static void lowpower_set_voltage_for_process(lowpower_dcdc_power_profile_enum dcdc_power_profile); +static lowpower_driver_interface_t *s_lowpowerDriver = (lowpower_driver_interface_t *)(0x130050e4); + +/** + * @brief Configures and enters in low power mode + * @param p_lowpower_cfg: pointer to a structure that contains all low power mode parameters + * @return Nothing + * + * !!! IMPORTANT NOTES : + * 1 - CPU Interrupt Enable registers are updated with p_lowpower_cfg->WAKEUPINT. They are NOT restored by the + * API. + * 2 - The Non Maskable Interrupt (NMI) should be disable before calling this API (otherwise, there is a risk + * of Dead Lock). + * 3 - The HARD FAULT handler should execute from SRAM. (The Hard fault handler should initiate a full chip + * reset) + */ +static void POWER_EnterLowPower(LPC_LOWPOWER_T *p_lowpower_cfg); + +/** + * @brief + * @param + * @return + */ +static void lf_set_dcdc_power_profile_low(void) +{ +#define DCDC_POWER_PROFILE_LOW_0_ADDRS (FLASH_NMPA_BASE + 0xE0U) // N4M (0x3FCE0U) / N4S (0x9FCE0U) +#define DCDC_POWER_PROFILE_LOW_1_ADDRS (FLASH_NMPA_BASE + 0xE4U) // N4M (0x3FCE4U) / N4S (0x9FCE4U) + + uint32_t dcdcTrimValue0 = (*((volatile unsigned int *)(DCDC_POWER_PROFILE_LOW_0_ADDRS))); + uint32_t dcdcTrimValue1 = (*((volatile unsigned int *)(DCDC_POWER_PROFILE_LOW_1_ADDRS))); + + if (0UL != (dcdcTrimValue0 & 0x1UL)) + { + PMC->DCDC0 = dcdcTrimValue0 >> 1; + PMC->DCDC1 = dcdcTrimValue1; + } +} + +/** + * @brief Configures and enters in low power mode + * @param : p_lowpower_cfg + * @return Nothing + */ +static void POWER_EnterLowPower(LPC_LOWPOWER_T *p_lowpower_cfg) +{ + /* PMC clk set to 12 MHZ */ + p_lowpower_cfg->CFG |= (uint32_t)LOWPOWER_CFG_SELCLOCK_12MHZ << LOWPOWER_CFG_SELCLOCK_INDEX; + + /* Enable Analog References fast wake-up in case of wake-up from a low power mode (DEEP SLEEP, POWER DOWN and DEEP + * POWER DOWN) and Hardware Pin reset */ + PMC->REFFASTWKUP = (PMC->REFFASTWKUP & (~PMC_REFFASTWKUP_LPWKUP_MASK) & (~PMC_REFFASTWKUP_HWWKUP_MASK)) | + PMC_REFFASTWKUP_LPWKUP(1) | PMC_REFFASTWKUP_HWWKUP(1); + + /* SRAM uses Voltage Scaling in all Low Power modes */ + PMC->SRAMCTRL = (PMC->SRAMCTRL & (~PMC_SRAMCTRL_SMB_MASK)) | PMC_SRAMCTRL_SMB(3); + + /* CPU Retention configuration : preserve the value of FUNCRETENTIONCTRL.RET_LENTH which is a Hardware defined + * parameter. */ + p_lowpower_cfg->CPURETCTRL = (SYSCON->FUNCRETENTIONCTRL & SYSCON_FUNCRETENTIONCTRL_RET_LENTH_MASK) | + (p_lowpower_cfg->CPURETCTRL & (~SYSCON_FUNCRETENTIONCTRL_RET_LENTH_MASK)); + + /* Switch System Clock to FRO12Mhz (the configuration before calling this function will not be restored back) */ + CLOCK_AttachClk(kFRO12M_to_MAIN_CLK); /* Switch main clock to FRO12MHz */ + CLOCK_SetClkDiv(kCLOCK_DivAhbClk, 1U, false); /* Main clock divided by 1 */ + SYSCON->FMCCR = (SYSCON->FMCCR & 0xFFFF0000UL) | 0x201AUL; /* Adjust FMC waiting time cycles */ + lf_set_dcdc_power_profile_low(); /* Align DCDC Power profile with the 12 MHz clock (DCDC Power Profile LOW) */ + + (*(s_lowpowerDriver->set_lowpower_mode))(p_lowpower_cfg); + + /* Restore the configuration of the MISCCTRL Register : LOWPWR_FLASH_BUF = 0, LDOMEMBLEEDDSLP = 0, LDOMEMHIGHZMODE = + * 0 */ + PMC->MISCCTRL &= (~PMC_MISCCTRL_LOWPWR_FLASH_BUF_MASK) & (~PMC_MISCCTRL_DISABLE_BLEED_MASK) & + (~PMC_MISCCTRL_LDOMEMHIGHZMODE_MASK); +} + +/** + * @brief Shut off the Flash and execute the _WFI(), then power up the Flash after wake-up event + * @param None + * @return Nothing + */ +void POWER_CycleCpuAndFlash(void) +{ + (*(s_lowpowerDriver->power_cycle_cpu_and_flash))(); +}; + +/** + * brief PMC Deep Sleep function call + * return nothing + */ +void POWER_EnterDeepSleep(uint32_t exclude_from_pd, + uint32_t sram_retention_ctrl, + uint64_t wakeup_interrupts, + uint32_t hardware_wake_ctrl) +{ + LPC_LOWPOWER_T lv_low_power_mode_cfg; /* Low Power Mode configuration structure */ + uint32_t cpu0_nmi_enable; + uint32_t cpu0_int_enable_0; + uint32_t cpu0_int_enable_1; + uint32_t dcdc_voltage; + uint32_t pmc_reset_ctrl; + /* Clear Low Power Mode configuration variable */ + (void)memset(&lv_low_power_mode_cfg, 0x0, sizeof(LPC_LOWPOWER_T)); + + /* Configure Low Power Mode configuration variable */ + lv_low_power_mode_cfg.CFG |= (uint32_t)LOWPOWER_CFG_LPMODE_DEEPSLEEP + << LOWPOWER_CFG_LPMODE_INDEX; /* DEEPSLEEP mode */ + + lf_get_deepsleep_core_supply_cfg(exclude_from_pd, &dcdc_voltage); + + if (((exclude_from_pd & (uint32_t)kPDRUNCFG_PD_USB1_PHY) != 0UL) && + ((exclude_from_pd & (uint32_t)kPDRUNCFG_PD_LDOUSBHS) != 0UL)) + { + /* USB High Speed is required as wake-up source in Deep Sleep mode: make sure LDO FLASH NV stays powered during + * deep-sleep */ + exclude_from_pd = exclude_from_pd | (uint32_t)kPDRUNCFG_PD_LDOFLASHNV; + } + + /* DCDC will be always used during Deep Sleep (instead of LDO Deep Sleep); Make sure LDO MEM & Analog references + * will stay powered, Shut down ROM */ + lv_low_power_mode_cfg.PDCTRL0 = (~exclude_from_pd & ~(uint32_t)kPDRUNCFG_PD_DCDC & ~(uint32_t)kPDRUNCFG_PD_LDOMEM & + ~(uint32_t)kPDRUNCFG_PD_BIAS) | + (uint32_t)kPDRUNCFG_PD_LDODEEPSLEEP | (uint32_t)kPDRUNCFG_PD_ROM; + + /* Voltage control in DeepSleep Low Power Modes */ + /* The Memories Voltage settings below are for voltage scaling */ + lv_low_power_mode_cfg.VOLTAGE = lf_set_ldo_ao_ldo_mem_voltage(LOWPOWER_CFG_LPMODE_POWERDOWN, dcdc_voltage); + + /* SRAM retention control during POWERDOWN */ + lv_low_power_mode_cfg.SRAMRETCTRL = sram_retention_ctrl; + + /* CPU Wake up & Interrupt sources control */ + lv_low_power_mode_cfg.WAKEUPINT = wakeup_interrupts; + lv_low_power_mode_cfg.WAKEUPSRC = wakeup_interrupts; + + /* Interrupts that allow DMA transfers with Flexcomm without waking up the Processor */ + if (0UL != (hardware_wake_ctrl & (LOWPOWER_HWWAKE_PERIPHERALS | LOWPOWER_HWWAKE_SDMA0 | LOWPOWER_HWWAKE_SDMA1))) + { + lv_low_power_mode_cfg.HWWAKE = (hardware_wake_ctrl & ~LOWPOWER_HWWAKE_FORCED) | LOWPOWER_HWWAKE_ENABLE_FRO192M; + } + + cpu0_nmi_enable = SYSCON->NMISRC & SYSCON_NMISRC_NMIENCPU0_MASK; /* Save the configuration of the NMI Register */ + SYSCON->NMISRC &= ~SYSCON_NMISRC_NMIENCPU0_MASK; /* Disable NMI of CPU0 */ + + /* Save the configuration of the CPU interrupt enable Registers (because they are overwritten inside the low power + * API */ + cpu0_int_enable_0 = NVIC->ISER[0]; + cpu0_int_enable_1 = NVIC->ISER[1]; + + pmc_reset_ctrl = PMC->RESETCTRL; + + if ((pmc_reset_ctrl & (PMC_RESETCTRL_BODCORERESETENA_SECURE_MASK | PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_MASK)) == + ((0x1UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_SHIFT) | + (0x1UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_SHIFT))) + { + /* BoD CORE reset is activated, so make sure BoD Core and Biasing won't be shutdown */ + lv_low_power_mode_cfg.PDCTRL0 &= ~(uint32_t)kPDRUNCFG_PD_BODCORE & ~(uint32_t)kPDRUNCFG_PD_BIAS; + } + if ((pmc_reset_ctrl & (PMC_RESETCTRL_BODVBATRESETENA_SECURE_MASK | PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_MASK)) == + ((0x1UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_SHIFT) | + (0x1UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_SHIFT))) + { + /* BoD VBAT reset is activated, so make sure BoD VBAT and Biasing won't be shutdown */ + lv_low_power_mode_cfg.PDCTRL0 &= ~(uint32_t)kPDRUNCFG_PD_BODVBAT & ~(uint32_t)kPDRUNCFG_PD_BIAS; + } + + /* Enter low power mode */ + POWER_EnterLowPower(&lv_low_power_mode_cfg); + + /* Restore the configuration of the NMI Register */ + SYSCON->NMISRC |= cpu0_nmi_enable; + + /* Restore the configuration of the CPU interrupt enable Registers (because they have been overwritten inside the + * low power API */ + NVIC->ISER[0] = cpu0_int_enable_0; + NVIC->ISER[1] = cpu0_int_enable_1; +} + +/** + * brief PMC power Down function call + * return nothing + */ +void POWER_EnterPowerDown(uint32_t exclude_from_pd, + uint32_t sram_retention_ctrl, + uint64_t wakeup_interrupts, + uint32_t cpu_retention_ctrl) +{ + LPC_LOWPOWER_T lv_low_power_mode_cfg; /* Low Power Mode configuration structure */ + uint32_t cpu0_nmi_enable; + uint32_t cpu0_int_enable_0; + uint32_t cpu0_int_enable_1; + uint64_t wakeup_src_int; + uint32_t pmc_reset_ctrl; + uint32_t rng_entropy_save[6]; + + uint32_t analog_ctrl_regs[12]; /* To store Analog Controller Regristers */ + + /* Clear Low Power Mode configuration variable */ + (void)memset(&lv_low_power_mode_cfg, 0x0, sizeof(LPC_LOWPOWER_T)); + + /* Configure Low Power Mode configuration variable */ + lv_low_power_mode_cfg.CFG |= (uint32_t)LOWPOWER_CFG_LPMODE_POWERDOWN + << LOWPOWER_CFG_LPMODE_INDEX; /* POWER DOWN mode */ + + /* Only FRO32K, XTAL32K, COMP, BIAS and LDO_MEM can be stay powered during POWERDOWN (valid from application point + * of view; Hardware allows BODVBAT, LDODEEPSLEEP and FRO1M to stay powered, that's why they are excluded below) */ + lv_low_power_mode_cfg.PDCTRL0 = (~exclude_from_pd) | (uint32_t)kPDRUNCFG_PD_BODVBAT | (uint32_t)kPDRUNCFG_PD_FRO1M | + (uint32_t)kPDRUNCFG_PD_LDODEEPSLEEP; + + /* @TODO Guillaume: add save/restore entropy during PowerDown */ + /* Entropy for RNG need to saved */ + if ((exclude_from_pd & (uint32_t)kPDRUNCFG_PD_RNG) != 0UL) + { + CLOCK_EnableClock(kCLOCK_Rng); + RESET_ClearPeripheralReset(kRNG_RST_SHIFT_RSTn); + for (int i = 0; i < 6; i++) + { + rng_entropy_save[i] = RNG->RANDOM_NUMBER; + } + } + + /* CPU0 retention Ctrl. + * For the time being, we do not allow customer to relocate the CPU retention area in SRAMX, meaning that the + * retention area range is [0x0400_6000 - 0x0400_6600] (beginning of RAMX2 for N4S) If required by customer, + * retention area range is [0x0400_2000 - 0x0400_2600] (beginning of RAMX2 for N4M) If required by customer, + * cpu_retention_ctrl[13:1] will be used for that to modify the default retention area + */ + lv_low_power_mode_cfg.CPURETCTRL = + (cpu_retention_ctrl & LOWPOWER_CPURETCTRL_ENA_MASK) | + ((((uint32_t)CPU_RETENTION_RAMX_STORAGE_START_ADDR >> 2UL) << LOWPOWER_CPURETCTRL_MEMBASE_INDEX) & + LOWPOWER_CPURETCTRL_MEMBASE_MASK); + if (0UL != (cpu_retention_ctrl & 0x1UL)) + { + /* Add RAMX2 for retention */ + sram_retention_ctrl |= LOWPOWER_SRAMRETCTRL_RETEN_RAMX2; + /* CPU retention is required: store Analog Controller Registers */ + analog_ctrl_regs[0] = ANACTRL->FRO192M_CTRL; + analog_ctrl_regs[1] = ANACTRL->ANALOG_CTRL_CFG; + analog_ctrl_regs[2] = ANACTRL->ADC_CTRL; + analog_ctrl_regs[3] = ANACTRL->XO32M_CTRL; + analog_ctrl_regs[4] = ANACTRL->BOD_DCDC_INT_CTRL; + analog_ctrl_regs[5] = ANACTRL->RINGO0_CTRL; + analog_ctrl_regs[6] = ANACTRL->RINGO1_CTRL; + analog_ctrl_regs[7] = ANACTRL->RINGO2_CTRL; + analog_ctrl_regs[8] = ANACTRL->LDO_XO32M; + analog_ctrl_regs[9] = ANACTRL->AUX_BIAS; + analog_ctrl_regs[10] = ANACTRL->USBHS_PHY_CTRL; + analog_ctrl_regs[11] = ANACTRL->USBHS_PHY_TRIM; + } + + /* SRAM retention control during POWERDOWN */ + lv_low_power_mode_cfg.SRAMRETCTRL = sram_retention_ctrl; + + /* Sanity check: If retention is required for any of SRAM instances, make sure LDO MEM will stay powered */ + if ((sram_retention_ctrl & 0x7FFFUL) != 0UL) + { + lv_low_power_mode_cfg.PDCTRL0 &= ~(uint32_t)kPDRUNCFG_PD_LDOMEM; + } + + /* Voltage control in Low Power Modes */ + /* The Memories Voltage settings below are for voltage scaling */ + lv_low_power_mode_cfg.VOLTAGE = lf_set_ldo_ao_ldo_mem_voltage(LOWPOWER_CFG_LPMODE_POWERDOWN, 0); + + /* CPU Wake up & Interrupt sources control : only WAKEUP_GPIO_GLOBALINT0, WAKEUP_GPIO_GLOBALINT1, WAKEUP_FLEXCOMM3, + * WAKEUP_ACMP_CAPT, WAKEUP_RTC_LITE_ALARM_WAKEUP, WAKEUP_OS_EVENT_TIMER, WAKEUP_ALLWAKEUPIOS */ + wakeup_src_int = (uint64_t)(WAKEUP_GPIO_GLOBALINT0 | WAKEUP_GPIO_GLOBALINT1 | WAKEUP_FLEXCOMM3 | WAKEUP_ACMP | + WAKEUP_RTC_LITE_ALARM_WAKEUP | WAKEUP_OS_EVENT_TIMER | WAKEUP_ALLWAKEUPIOS); + lv_low_power_mode_cfg.WAKEUPINT = wakeup_interrupts & wakeup_src_int; + lv_low_power_mode_cfg.WAKEUPSRC = wakeup_interrupts & wakeup_src_int; + + cpu0_nmi_enable = SYSCON->NMISRC & SYSCON_NMISRC_NMIENCPU0_MASK; /* Save the configuration of the NMI Register */ + SYSCON->NMISRC &= ~SYSCON_NMISRC_NMIENCPU0_MASK; /* Disable NMI of CPU0 */ + + /* Save the configuration of the CPU interrupt enable Registers (because they are overwritten inside the low power + * API */ + cpu0_int_enable_0 = NVIC->ISER[0]; + cpu0_int_enable_1 = NVIC->ISER[1]; + + /* Disable BoD VBAT and BoD Core resets */ + /* BOD VBAT disable reset */ + pmc_reset_ctrl = + PMC->RESETCTRL & (~(PMC_RESETCTRL_BODVBATRESETENA_SECURE_MASK | PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_MASK)); + pmc_reset_ctrl |= (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_SHIFT) | + (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_SHIFT); + + /* BOD CORE disable reset */ + pmc_reset_ctrl &= (~(PMC_RESETCTRL_BODCORERESETENA_SECURE_MASK | PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_MASK)); + pmc_reset_ctrl |= (0x2UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_SHIFT) | + (0x2UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_SHIFT); + + PMC->RESETCTRL = pmc_reset_ctrl; + + /* Enter low power mode */ + POWER_EnterLowPower(&lv_low_power_mode_cfg); + + /*** We'll reach this point in case of POWERDOWN with CPU retention or if the POWERDOWN has not been taken (for + instance because an interrupt is pending). In case of CPU retention, assumption is that the SRAM containing the + stack used to call this function shall be preserved during low power ***/ + +#if defined(PUF_SRAM_CTRL_CFG_ENABLE_MASK) + /* Following code is to reset PUF to remove over consumption */ + /* Enable PUF register clock to access register */ + SYSCON->AHBCLKCTRLSET[2] = SYSCON_AHBCLKCTRL2_PUF_MASK; + /* Release PUF reset */ + SYSCON->PRESETCTRLCLR[2] = SYSCON_PRESETCTRL2_PUF_RST_MASK; + /* Enable PUF SRAM */ + PUF_SRAM_CTRL->CFG |= PUF_SRAM_CTRL_CFG_ENABLE_MASK | PUF_SRAM_CTRL_CFG_CKGATING_MASK; + + /* Disable PUF register clock. */ + // Delaying the line of code below until the PUF State Machine execution is completed: + // Shuting down the clock to early will prevent the state machine from reaching the end. + // => Wait for status bit in PUF Controller Registers before stop PUF clock. + while (0UL == (PUF_SRAM_CTRL->INT_STATUS & PUF_SRAM_CTRL_INT_STATUS_READY_MASK)) + { + } + + SYSCON->AHBCLKCTRLCLR[2] = SYSCON_AHBCLKCTRL2_PUF_MASK; +#endif + /* Restore the configuration of the NMI Register */ + SYSCON->NMISRC |= cpu0_nmi_enable; + + /* Restore PMC RESETCTRL register */ + PMC->RESETCTRL = pmc_reset_ctrl; + + /* Restore the configuration of the CPU interrupt enable Registers (because they have been overwritten inside the + * low power API */ + NVIC->ISER[0] = cpu0_int_enable_0; + NVIC->ISER[1] = cpu0_int_enable_1; + + if ((cpu_retention_ctrl & 0x1UL) != 0UL) + { + /* Restore Analog Controller Registers */ + ANACTRL->FRO192M_CTRL = analog_ctrl_regs[0] | ANACTRL_FRO192M_CTRL_WRTRIM_MASK; + ANACTRL->ANALOG_CTRL_CFG = analog_ctrl_regs[1]; + ANACTRL->ADC_CTRL = analog_ctrl_regs[2]; + ANACTRL->XO32M_CTRL = analog_ctrl_regs[3]; + ANACTRL->BOD_DCDC_INT_CTRL = analog_ctrl_regs[4]; + ANACTRL->RINGO0_CTRL = analog_ctrl_regs[5]; + ANACTRL->RINGO1_CTRL = analog_ctrl_regs[6]; + ANACTRL->RINGO2_CTRL = analog_ctrl_regs[7]; + ANACTRL->LDO_XO32M = analog_ctrl_regs[8]; + ANACTRL->AUX_BIAS = analog_ctrl_regs[9]; + ANACTRL->USBHS_PHY_CTRL = analog_ctrl_regs[10]; + ANACTRL->USBHS_PHY_TRIM = analog_ctrl_regs[11]; + } + + /* @TODO Guillaume: add save/restore entropy during PowerDown */ + /* Restore Entropy for RNG */ + if ((exclude_from_pd & (uint32_t)kPDRUNCFG_PD_RNG) != 0UL) + { + RNG->POWERDOWN &= ~RNG_POWERDOWN_POWERDOWN_MASK; + for (int i = 0; i < 6; i++) + { + RNG->ENTROPY_INJECT = rng_entropy_save[i]; + } + } +} + +/** + * brief PMC Deep Sleep Power Down function call + * return nothing + */ +void POWER_EnterDeepPowerDown(uint32_t exclude_from_pd, + uint32_t sram_retention_ctrl, + uint64_t wakeup_interrupts, + uint32_t wakeup_io_ctrl) +{ + LPC_LOWPOWER_T lv_low_power_mode_cfg; /* Low Power Mode configuration structure */ + uint32_t cpu0_nmi_enable; + uint32_t cpu0_int_enable_0; + uint32_t cpu0_int_enable_1; + uint32_t pmc_reset_ctrl; + + /* Clear Low Power Mode configuration variable */ + (void)memset(&lv_low_power_mode_cfg, 0x0, sizeof(LPC_LOWPOWER_T)); + + /* Configure Low Power Mode configuration variable */ + lv_low_power_mode_cfg.CFG |= (uint32_t)LOWPOWER_CFG_LPMODE_DEEPPOWERDOWN + << LOWPOWER_CFG_LPMODE_INDEX; /* DEEP POWER DOWN mode */ + + /* Only FRO32K, XTAL32K and LDO_MEM can be stay powered during DEEPPOWERDOWN (valid from application point of view; + * Hardware allows BODVBAT, BIAS FRO1M and COMP to stay powered, that's why they are excluded below) */ + lv_low_power_mode_cfg.PDCTRL0 = (~exclude_from_pd) | (uint32_t)kPDRUNCFG_PD_BIAS | (uint32_t)kPDRUNCFG_PD_BODVBAT | + (uint32_t)kPDRUNCFG_PD_FRO1M | (uint32_t)kPDRUNCFG_PD_COMP; + + /* SRAM retention control during DEEPPOWERDOWN */ + /* RAM00 used by ROM code to restart. */ + sram_retention_ctrl = sram_retention_ctrl & (~(LOWPOWER_SRAMRETCTRL_RETEN_RAM00)); + + /* SRAM retention control during DEEPPOWERDOWN */ + lv_low_power_mode_cfg.SRAMRETCTRL = sram_retention_ctrl; + + /* Sanity check: If retention is required for any of SRAM instances, make sure LDO MEM will stay powered */ + if ((sram_retention_ctrl & 0x7FFFUL) != 0UL) + { + lv_low_power_mode_cfg.PDCTRL0 &= ~(uint32_t)kPDRUNCFG_PD_LDOMEM; + } + + /* Voltage control in Low Power Modes */ + /* The Memories Voltage settings below are for voltage scaling */ + lv_low_power_mode_cfg.VOLTAGE = lf_set_ldo_ao_ldo_mem_voltage(LOWPOWER_CFG_LPMODE_DEEPPOWERDOWN, 0); + + lv_low_power_mode_cfg.WAKEUPINT = + wakeup_interrupts & (WAKEUP_RTC_LITE_ALARM_WAKEUP | + WAKEUP_OS_EVENT_TIMER); /* CPU Wake up sources control : only WAKEUP_RTC_LITE_ALARM_WAKEUP, + WAKEUP_OS_EVENT_TIMER */ + lv_low_power_mode_cfg.WAKEUPSRC = + wakeup_interrupts & + (WAKEUP_RTC_LITE_ALARM_WAKEUP | WAKEUP_OS_EVENT_TIMER | + WAKEUP_ALLWAKEUPIOS); /*!< Hardware Wake up sources control: : only WAKEUP_RTC_LITE_ALARM_WAKEUP, + WAKEUP_OS_EVENT_TIMER and WAKEUP_ALLWAKEUPIOS */ + + /* Wake up I/O sources */ + lv_low_power_mode_cfg.WAKEUPIOSRC = lf_wakeup_io_ctrl(wakeup_io_ctrl); + + cpu0_nmi_enable = SYSCON->NMISRC & SYSCON_NMISRC_NMIENCPU0_MASK; /* Save the configuration of the NMI Register */ + SYSCON->NMISRC &= ~SYSCON_NMISRC_NMIENCPU0_MASK; /* Disable NMI of CPU0 */ + + /* Save the configuration of the CPU interrupt enable Registers (because they are overwritten inside the low power + * API */ + cpu0_int_enable_0 = NVIC->ISER[0]; + cpu0_int_enable_1 = NVIC->ISER[1]; + + /* Save the configuration of the PMC RESETCTRL register */ + /* BOD VBAT disable reset */ + pmc_reset_ctrl = + PMC->RESETCTRL & (~(PMC_RESETCTRL_BODVBATRESETENA_SECURE_MASK | PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_MASK)); + pmc_reset_ctrl |= (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_SHIFT) | + (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_SHIFT); + + /* BOD CORE disable reset */ + pmc_reset_ctrl &= (~(PMC_RESETCTRL_BODCORERESETENA_SECURE_MASK | PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_MASK)); + pmc_reset_ctrl |= (0x2UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_SHIFT) | + (0x2UL << PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_SHIFT); + + PMC->RESETCTRL = pmc_reset_ctrl; + + /* Enter low power mode */ + POWER_EnterLowPower(&lv_low_power_mode_cfg); + + /*** We'll reach this point ONLY and ONLY if the DEEPPOWERDOWN has not been taken (for instance because an RTC or + * OSTIMER interrupt is pending) ***/ + + /* Restore the configuration of the NMI Register */ + SYSCON->NMISRC |= cpu0_nmi_enable; + + /* Restore PMC RESETCTRL register */ + PMC->RESETCTRL = pmc_reset_ctrl; + + /* Restore the configuration of the CPU interrupt enable Registers (because they have been overwritten inside the + * low power API */ + NVIC->ISER[0] = cpu0_int_enable_0; + NVIC->ISER[1] = cpu0_int_enable_1; +} + +/** + * brief PMC Sleep function call + * return nothing + */ +void POWER_EnterSleep(void) +{ + uint32_t pmsk; + pmsk = __get_PRIMASK(); + __disable_irq(); + SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; + __WFI(); + __set_PRIMASK(pmsk); +} + +/** + * @brief Get Digital Core logic supply source to be used during Deep Sleep. + * @param [in] exclude_from_pd: COmpoenents NOT to be powered down during Deep Sleep + * @param [out] core_supply: 0 = LDO DEEPSLEEP will be used / 1 = DCDC will be used + * @param [out] dcdc_voltage: as defined by V_DCDC_* in fsl_power.h + + * @return Nothing + */ +static void lf_get_deepsleep_core_supply_cfg(uint32_t exclude_from_pd, uint32_t *dcdc_voltage) +{ + *dcdc_voltage = V_DCDC_0P950; /* Default value */ + + if (((exclude_from_pd & (uint32_t)kPDRUNCFG_PD_USB1_PHY) != 0) && + ((exclude_from_pd & (uint32_t)kPDRUNCFG_PD_LDOUSBHS) != 0)) + { + /* USB High Speed is required as wake-up source in Deep Sleep mode */ + PMC->MISCCTRL |= PMC_MISCCTRL_LOWPWR_FLASH_BUF_MASK; /* Force flash buffer in low power mode */ + *dcdc_voltage = V_DCDC_1P000; /* Set DCDC voltage to be 1.000 V (USB HS IP cannot work below 0.990 V) */ + } +} + +/** + * @brief + * @param + * @return + */ +static uint32_t lf_set_ldo_ao_ldo_mem_voltage(uint32_t p_lp_mode, uint32_t p_dcdc_voltage) +{ +#define FLASH_NMPA_LDO_AO_ADDRS (FLASH_NMPA_BASE + 0x0F4U) // N4M (0x3FCF4U) / N4S (0x9FCF4U) +#define FLASH_NMPA_LDO_AO_DSLP_TRIM_VALID_MASK (0x100U) +#define FLASH_NMPA_LDO_AO_DSLP_TRIM_MASK (0x3E00U) +#define FLASH_NMPA_LDO_AO_DSLP_TRIM_SHIFT (9U) +#define FLASH_NMPA_LDO_AO_PDWN_TRIM_VALID_MASK (0x10000U) +#define FLASH_NMPA_LDO_AO_PDWN_TRIM_MASK (0x3E0000U) +#define FLASH_NMPA_LDO_AO_PDWN_TRIM_SHIFT (17U) +#define FLASH_NMPA_LDO_AO_DPDW_TRIM_VALID_MASK (0x1000000U) +#define FLASH_NMPA_LDO_AO_DPDW_TRIM_MASK (0x3E000000U) +#define FLASH_NMPA_LDO_AO_DPDW_TRIM_SHIFT (25U) + + uint32_t ldo_ao_trim, voltage; + uint32_t lv_v_ldo_pmu, lv_v_ldo_pmu_boost; + + ldo_ao_trim = (*((volatile unsigned int *)(FLASH_NMPA_LDO_AO_ADDRS))); + + switch (p_lp_mode) + { + case LOWPOWER_CFG_LPMODE_DEEPSLEEP: + { + if ((ldo_ao_trim & FLASH_NMPA_LDO_AO_DSLP_TRIM_VALID_MASK) != 0UL) + { + /* Apply settings coming from Flash */ + lv_v_ldo_pmu = (ldo_ao_trim & FLASH_NMPA_LDO_AO_DSLP_TRIM_MASK) >> FLASH_NMPA_LDO_AO_DSLP_TRIM_SHIFT; + lv_v_ldo_pmu_boost = lv_v_ldo_pmu - 2UL; /* - 50 mV */ + } + else + { + /* Apply default settings */ + lv_v_ldo_pmu = (uint32_t)V_AO_0P900; + lv_v_ldo_pmu_boost = (uint32_t)V_AO_0P850; + } + } + break; + + case LOWPOWER_CFG_LPMODE_POWERDOWN: + { + if ((ldo_ao_trim & FLASH_NMPA_LDO_AO_PDWN_TRIM_VALID_MASK) != 0UL) + { + /* Apply settings coming from Flash */ + lv_v_ldo_pmu = (ldo_ao_trim & FLASH_NMPA_LDO_AO_PDWN_TRIM_MASK) >> FLASH_NMPA_LDO_AO_PDWN_TRIM_SHIFT; + lv_v_ldo_pmu_boost = lv_v_ldo_pmu - 2UL; /* - 50 mV */ + } + else + { + /* Apply default settings */ + lv_v_ldo_pmu = (uint32_t)V_AO_0P800; + lv_v_ldo_pmu_boost = (uint32_t)V_AO_0P750; + } + } + break; + + case LOWPOWER_CFG_LPMODE_DEEPPOWERDOWN: + { + if ((ldo_ao_trim & FLASH_NMPA_LDO_AO_DPDW_TRIM_VALID_MASK) != 0UL) + { + /* Apply settings coming from Flash */ + lv_v_ldo_pmu = (ldo_ao_trim & FLASH_NMPA_LDO_AO_DPDW_TRIM_MASK) >> FLASH_NMPA_LDO_AO_DPDW_TRIM_SHIFT; + lv_v_ldo_pmu_boost = lv_v_ldo_pmu - 2UL; /* - 50 mV */ + } + else + { + /* Apply default settings */ + lv_v_ldo_pmu = (uint32_t)V_AO_0P800; + lv_v_ldo_pmu_boost = (uint32_t)V_AO_0P750; + } + } + break; + + default: + /* Should never reach this point */ + lv_v_ldo_pmu = (uint32_t)V_AO_1P100; + lv_v_ldo_pmu_boost = (uint32_t)V_AO_1P050; + break; + } + + /* The Memories Voltage settings below are for voltage scaling */ + voltage = + (lv_v_ldo_pmu << LOWPOWER_VOLTAGE_LDO_PMU_INDEX) | /* */ + (lv_v_ldo_pmu_boost << LOWPOWER_VOLTAGE_LDO_PMU_BOOST_INDEX) | /* */ + ((uint32_t)V_AO_0P750 << LOWPOWER_VOLTAGE_LDO_MEM_INDEX) | /* Set to 0.75V (voltage Scaling) */ + ((uint32_t)V_AO_0P700 << LOWPOWER_VOLTAGE_LDO_MEM_BOOST_INDEX) | /* Set to 0.7V (voltage Scaling) */ + ((uint32_t)V_DEEPSLEEP_0P900 + << LOWPOWER_VOLTAGE_LDO_DEEP_SLEEP_INDEX) | /* Set to 0.90 V (Not used because LDO_DEEP_SLEEP is disabled)*/ + (p_dcdc_voltage << LOWPOWER_VOLTAGE_DCDC_INDEX) /* */ + ; + + return (voltage); +} + +/** + * @brief + * @param + * @return + */ +static uint32_t lf_wakeup_io_ctrl(uint32_t p_wakeup_io_ctrl) +{ + uint32_t wake_up_type; + uint32_t wakeup_io_ctrl_reg; + uint8_t use_external_pullupdown = 0; + + /* Configure Pull up & Pull down based on the required wake-up edge */ + CLOCK_EnableClock(kCLOCK_Iocon); + + wakeup_io_ctrl_reg = 0UL; + + /* Wake-up I/O 0 */ + wake_up_type = (p_wakeup_io_ctrl & 0x3UL) >> LOWPOWER_WAKEUPIOSRC_PIO0_INDEX; + wakeup_io_ctrl_reg |= (wake_up_type << LOWPOWER_WAKEUPIOSRC_PIO0_INDEX); + use_external_pullupdown = (uint8_t)((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO0_USEEXTERNALPULLUPDOWN_MASK) >> + LOWPOWER_WAKEUPIO_PIO0_USEEXTERNALPULLUPDOWN_INDEX); + + if (use_external_pullupdown == 0UL) + { + if ((wake_up_type == 1UL) || (wake_up_type == 3UL)) + { + /* Rising edge and both rising and falling edges */ + IOCON->PIO[1][1] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(1); /* Pull down */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX); + } + else + { + if (wake_up_type == 2UL) + { + /* Falling edge only */ + IOCON->PIO[1][1] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(2); /* Pull up */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX); + } + else + { + /* Wake-up I/O is disabled : set it as required by the user */ + if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO0_DISABLEPULLUPDOWN_MASK) != 0UL) + { + /* Wake-up I/O is configured as Plain Input */ + // @TODO not used p_wakeup_io_ctrl &= ~LOWPOWER_WAKEUPIO_PIO0_PULLUPDOWN_MASK; + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX); + } + else + { + /* Wake-up I/O is configured as pull-up or pull-down */ + // @TODO update for mask name + if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO0_PULLUPDOWN_MASK) != 0UL) + { + /* Wake-up I/O is configured as pull-up */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX); + } + else + { + /* Wake-up I/O is configured as pull-down */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX); + } + } + } + } + } + else + { + wakeup_io_ctrl_reg |= ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX); + } + + /* Wake-up I/O 1 */ + wake_up_type = (p_wakeup_io_ctrl & 0xCUL) >> LOWPOWER_WAKEUPIOSRC_PIO1_INDEX; + wakeup_io_ctrl_reg |= (wake_up_type << LOWPOWER_WAKEUPIOSRC_PIO1_INDEX); + use_external_pullupdown = (uint8_t)((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO1_USEEXTERNALPULLUPDOWN_MASK) >> + LOWPOWER_WAKEUPIO_PIO1_USEEXTERNALPULLUPDOWN_INDEX); + + if (use_external_pullupdown == 0UL) + { + if ((wake_up_type == 1UL) || (wake_up_type == 3UL)) + { + /* Rising edge and both rising and falling edges */ + IOCON->PIO[0][28] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(1); /* Pull down */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX); + } + else + { + if (wake_up_type == 2UL) + { + /* Falling edge only */ + IOCON->PIO[0][28] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(2); /* Pull up */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX); + } + else + { + /* Wake-up I/O is disabled : set it as required by the user */ + if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO1_DISABLEPULLUPDOWN_MASK) != 0UL) + { + /* Wake-up I/O is configured as Plain Input */ + // @TODO not used p_wakeup_io_ctrl &= ~LOWPOWER_WAKEUPIO_PIO1_PULLUPDOWN_MASK; + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX); + } + else + { + /* Wake-up I/O is configured as pull-up or pull-down */ + // @TODO update for mask name + if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO1_PULLUPDOWN_MASK) != 0UL) + { + /* Wake-up I/O is configured as pull-up */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX); + } + else + { + /* Wake-up I/O is configured as pull-down */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX); + } + } + } + } + } + else + { + wakeup_io_ctrl_reg |= ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX); + } + + /* Wake-up I/O 2 */ + wake_up_type = (p_wakeup_io_ctrl & 0x30UL) >> LOWPOWER_WAKEUPIOSRC_PIO2_INDEX; + wakeup_io_ctrl_reg |= (wake_up_type << LOWPOWER_WAKEUPIOSRC_PIO2_INDEX); + use_external_pullupdown = (uint8_t)((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO2_USEEXTERNALPULLUPDOWN_MASK) >> + LOWPOWER_WAKEUPIO_PIO2_USEEXTERNALPULLUPDOWN_INDEX); + + if (use_external_pullupdown == 0UL) + { + if ((wake_up_type == 1UL) || (wake_up_type == 3UL)) + { + /* Rising edge and both rising and falling edges */ + IOCON->PIO[1][18] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(1); /* Pull down */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX); + } + else + { + if (wake_up_type == 2UL) + { + /* Falling edge only */ + IOCON->PIO[1][18] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(2); /* Pull up */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX); + } + else + { + /* Wake-up I/O is disabled : set it as required by the user */ + if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO2_DISABLEPULLUPDOWN_MASK) != 0UL) + { + /* Wake-up I/O is configured as Plain Input */ + // @TODO not used p_wakeup_io_ctrl &= ~LOWPOWER_WAKEUPIO_PIO2_PULLUPDOWN_MASK; + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX); + } + else + { + /* Wake-up I/O is configured as pull-up or pull-down */ + // @TODO update for mask name + if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO2_PULLUPDOWN_MASK) != 0UL) + { + /* Wake-up I/O is configured as pull-up */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX); + } + else + { + /* Wake-up I/O is configured as pull-down */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX); + } + } + } + } + } + else + { + wakeup_io_ctrl_reg |= ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX); + } + + /* Wake-up I/O 3 */ + wake_up_type = (p_wakeup_io_ctrl & 0xC0UL) >> LOWPOWER_WAKEUPIOSRC_PIO3_INDEX; + wakeup_io_ctrl_reg |= (wake_up_type << LOWPOWER_WAKEUPIOSRC_PIO3_INDEX); + use_external_pullupdown = (uint8_t)((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO3_USEEXTERNALPULLUPDOWN_MASK) >> + LOWPOWER_WAKEUPIO_PIO3_USEEXTERNALPULLUPDOWN_INDEX); + + if (use_external_pullupdown == 0UL) + { + if ((wake_up_type == 1UL) || (wake_up_type == 3UL)) + { + /* Rising edge and both rising and falling edges */ + IOCON->PIO[1][30] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(1); /* Pull down */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX); + } + else + { + if (wake_up_type == 2UL) + { + /* Falling edge only */ + IOCON->PIO[1][30] = IOCON_PIO_DIGIMODE(1) | IOCON_PIO_MODE(2); /* Pull up */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX); + } + else + { + /* Wake-up I/O is disabled : set it as required by the user */ + if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO3_DISABLEPULLUPDOWN_MASK) != 0UL) + { + /* Wake-up I/O is configured as Plain Input */ + // @TODO not used p_wakeup_io_ctrl &= ~LOWPOWER_WAKEUPIO_PIO3_PULLUPDOWN_MASK; + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX); + } + else + { + /* Wake-up I/O is configured as pull-up or pull-down */ + // @TODO update for mask name + if ((p_wakeup_io_ctrl & LOWPOWER_WAKEUPIO_PIO3_PULLUPDOWN_MASK) != 0UL) + { + /* Wake-up I/O is configured as pull-up */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX); + } + else + { + /* Wake-up I/O is configured as pull-down */ + wakeup_io_ctrl_reg |= + ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX); + } + } + } + } + } + else + { + wakeup_io_ctrl_reg |= ((uint32_t)LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN << LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX); + } + + return (wakeup_io_ctrl_reg); +} + +/** + * @brief + * @param + * @return + */ +static uint8_t CLOCK_u8OscCapConvert(uint8_t u8OscCap, uint8_t u8CapBankDiscontinuity) +{ + /* Compensate for discontinuity in the capacitor banks */ + if (u8OscCap < 64U) + { + if (u8OscCap >= u8CapBankDiscontinuity) + { + u8OscCap -= u8CapBankDiscontinuity; + } + else + { + u8OscCap = 0U; + } + } + else + { + if (u8OscCap <= (127U - u8CapBankDiscontinuity)) + { + u8OscCap += u8CapBankDiscontinuity; + } + else + { + u8OscCap = 127U; + } + } + return u8OscCap; +} + +/** + * @brief Described in fsl_common.h + * @param + * @return + */ +static void lowpower_set_system_voltage(uint32_t system_voltage_mv) +{ + /* + * Set system voltage + */ + uint32_t lv_ldo_ao = (uint32_t)V_AO_1P100; /* */ + uint32_t lv_ldo_ao_boost = (uint32_t)V_AO_1P150; /* */ + uint32_t lv_dcdc = (uint32_t)V_DCDC_1P100; /* */ + + if (system_voltage_mv <= 950UL) + { + lv_dcdc = (uint32_t)V_DCDC_0P950; + lv_ldo_ao = (uint32_t)V_AO_0P960; + lv_ldo_ao_boost = (uint32_t)V_AO_1P010; + } + else if (system_voltage_mv <= 975UL) + { + lv_dcdc = (uint32_t)V_DCDC_0P975; + lv_ldo_ao = (uint32_t)V_AO_0P980; + lv_ldo_ao_boost = (uint32_t)V_AO_1P030; + } + else if (system_voltage_mv <= 1000UL) + { + lv_dcdc = (uint32_t)V_DCDC_1P000; + lv_ldo_ao = (uint32_t)V_AO_1P000; + lv_ldo_ao_boost = (uint32_t)V_AO_1P050; + } + else if (system_voltage_mv <= 1025UL) + { + lv_dcdc = (uint32_t)V_DCDC_1P025; + lv_ldo_ao = (uint32_t)V_AO_1P030; + lv_ldo_ao_boost = (uint32_t)V_AO_1P080; + } + else if (system_voltage_mv <= 1050UL) + { + lv_dcdc = (uint32_t)V_DCDC_1P050; + lv_ldo_ao = (uint32_t)V_AO_1P060; + lv_ldo_ao_boost = (uint32_t)V_AO_1P110; + } + else if (system_voltage_mv <= 1075UL) + { + lv_dcdc = (uint32_t)V_DCDC_1P075; + lv_ldo_ao = (uint32_t)V_AO_1P080; + lv_ldo_ao_boost = (uint32_t)V_AO_1P130; + } + else if (system_voltage_mv <= 1100UL) + { + lv_dcdc = (uint32_t)V_DCDC_1P100; + lv_ldo_ao = (uint32_t)V_AO_1P100; + lv_ldo_ao_boost = (uint32_t)V_AO_1P150; + } + else if (system_voltage_mv <= 1125UL) + { + lv_dcdc = (uint32_t)V_DCDC_1P125; + lv_ldo_ao = (uint32_t)V_AO_1P130; + lv_ldo_ao_boost = (uint32_t)V_AO_1P160; + } + else if (system_voltage_mv <= 1150UL) + { + lv_dcdc = (uint32_t)V_DCDC_1P150; + lv_ldo_ao = (uint32_t)V_AO_1P160; + lv_ldo_ao_boost = (uint32_t)V_AO_1P220; + } + else if (system_voltage_mv <= 1175UL) + { + lv_dcdc = (uint32_t)V_DCDC_1P175; + lv_ldo_ao = (uint32_t)V_AO_1P160; + lv_ldo_ao_boost = (uint32_t)V_AO_1P220; + } + else + { + lv_dcdc = (uint32_t)V_DCDC_1P200; + lv_ldo_ao = (uint32_t)V_AO_1P160; + lv_ldo_ao_boost = (uint32_t)V_AO_1P220; + } + + /* Set up LDO Always-On voltages */ + PMC->LDOPMU = (PMC->LDOPMU & (~PMC_LDOPMU_VADJ_MASK) & (~PMC_LDOPMU_VADJ_BOOST_MASK)) | PMC_LDOPMU_VADJ(lv_ldo_ao) | + PMC_LDOPMU_VADJ_BOOST(lv_ldo_ao_boost); + + /* Set up DCDC voltage */ + PMC->DCDC0 = (PMC->DCDC0 & (~PMC_DCDC0_VOUT_MASK)) | PMC_DCDC0_VOUT(lv_dcdc); +} + +/** + * @brief Described in fsl_common.h + * @param + * @return + */ +static void lowpower_set_dcdc_power_profile(lowpower_dcdc_power_profile_enum dcdc_power_profile) +{ +#define FLASH_NMPA_DCDC_POWER_PROFILE_LOW_0_ADDRS (FLASH_NMPA_BASE + 0xE0U) +#define FLASH_NMPA_DCDC_POWER_PROFILE_LOW_1_ADDRS (FLASH_NMPA_BASE + 0xE4U) +#define FLASH_NMPA_DCDC_POWER_PROFILE_MEDIUM_0_ADDRS (FLASH_NMPA_BASE + 0xE8U) +#define FLASH_NMPA_DCDC_POWER_PROFILE_MEDIUM_1_ADDRS (FLASH_NMPA_BASE + 0xECU) +#define FLASH_NMPA_DCDC_POWER_PROFILE_HIGH_0_ADDRS (FLASH_NMPA_BASE + 0xD8U) +#define FLASH_NMPA_DCDC_POWER_PROFILE_HIGH_1_ADDRS (FLASH_NMPA_BASE + 0xDCU) + + const uint32_t PMC_DCDC0_DEFAULT = 0x010C4E68; + const uint32_t PMC_DCDC1_DEFAULT = 0x01803A98; + + uint32_t dcdcTrimValue0; + uint32_t dcdcTrimValue1; + + switch (dcdc_power_profile) + { + case DCDC_POWER_PROFILE_LOW: + /* Low */ + dcdcTrimValue0 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_LOW_0_ADDRS))); + dcdcTrimValue1 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_LOW_1_ADDRS))); + + if (0UL != (dcdcTrimValue0 & 0x1UL)) + { + dcdcTrimValue0 = dcdcTrimValue0 >> 1; + + PMC->DCDC0 = dcdcTrimValue0; + PMC->DCDC1 = dcdcTrimValue1; +#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1)) + PRINTF( + "\nINFO : DCDC Power Profile set to " + "LOW" + "\n"); +#endif + } + break; + + case DCDC_POWER_PROFILE_MEDIUM: + /* Medium */ + dcdcTrimValue0 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_MEDIUM_0_ADDRS))); + dcdcTrimValue1 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_MEDIUM_1_ADDRS))); + + if (0UL != (dcdcTrimValue0 & 0x1UL)) + { + dcdcTrimValue0 = dcdcTrimValue0 >> 1; + + PMC->DCDC0 = dcdcTrimValue0; + PMC->DCDC1 = dcdcTrimValue1; +#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1)) + PRINTF( + "\nINFO : DCDC Power Profile set to " + "MEDIUM" + "\n"); +#endif + } + break; + + case DCDC_POWER_PROFILE_HIGH: + /* High */ + dcdcTrimValue0 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_HIGH_0_ADDRS))); + dcdcTrimValue1 = (*((volatile unsigned int *)(FLASH_NMPA_DCDC_POWER_PROFILE_HIGH_1_ADDRS))); + + if (0UL != (dcdcTrimValue0 & 0x1UL)) + { + dcdcTrimValue0 = dcdcTrimValue0 >> 1; + + PMC->DCDC0 = dcdcTrimValue0; + PMC->DCDC1 = dcdcTrimValue1; +#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1)) + PRINTF( + "\nINFO : DCDC Power Profile set to " + "HIGH" + "\n"); +#endif + } + break; + + default: + /* Low */ + PMC->DCDC0 = PMC_DCDC0_DEFAULT; + PMC->DCDC1 = PMC_DCDC1_DEFAULT; +#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1)) + PRINTF( + "\nINFO : DCDC Power Profile set to " + "LOW" + "\n"); +#endif + break; + } +} + +/** + * @brief + * @param + * @return + */ +static lowpower_process_corner_enum lowpower_get_part_process_corner(void) +{ +#define FLASH_NMPA_PVT_MONITOR_0_RINGO_ADDRS (FLASH_NMPA_BASE + 0x130U) +#define FLASH_NMPA_PVT_MONITOR_1_RINGO_ADDRS (FLASH_NMPA_BASE + 0x140U) + + lowpower_process_corner_enum part_process_corner; + uint32_t pvt_ringo_hz; + uint32_t pvt_ringo_0 = (*((volatile unsigned int *)(FLASH_NMPA_PVT_MONITOR_0_RINGO_ADDRS))); + uint32_t pvt_ringo_1 = (*((volatile unsigned int *)(FLASH_NMPA_PVT_MONITOR_1_RINGO_ADDRS))); + + /* + * Check that the PVT Monitors Trimmings in flash are valid. + */ + if (0UL != (pvt_ringo_0 & 0x1UL)) + { + /* PVT Trimmings in Flash are valid */ + pvt_ringo_0 = pvt_ringo_0 >> 1; + } + else + { + /* PVT Trimmings in Flash are NOT valid (average value assumed) */ + pvt_ringo_0 = PROCESS_NNN_AVG_HZ; + } + + if (0UL != (pvt_ringo_1 & 0x1UL)) + { + /* PVT Trimmings in Flash are valid */ + pvt_ringo_1 = pvt_ringo_1 >> 1; + } + else + { + /* PVT Trimmings in Flash are NOT valid (average value assumed) */ + pvt_ringo_1 = PROCESS_NNN_AVG_HZ; + } + + if (pvt_ringo_1 <= pvt_ringo_0) + { + pvt_ringo_hz = pvt_ringo_1; + } + else + { + pvt_ringo_hz = pvt_ringo_0; + } + + /* + * Determine the process corner based on the value of the Ring Oscillator frequency + */ + if (pvt_ringo_hz <= PROCESS_NNN_MIN_HZ) + { + /* SSS Process Corner */ + part_process_corner = PROCESS_CORNER_SSS; +#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1)) + PRINTF( + "\nINFO : Process Corner : " + "SSS" + "\n"); +#endif + } + else + { + if (pvt_ringo_hz <= PROCESS_NNN_MAX_HZ) + { + /* NNN Process Corner */ + part_process_corner = PROCESS_CORNER_NNN; +#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1)) + PRINTF( + "\nINFO : Process Corner : " + "NNN" + "\n"); +#endif + } + else + { + /* FFF Process Corner */ + part_process_corner = PROCESS_CORNER_FFF; +#if (defined(NIOBE_DEBUG_LEVEL) && (NIOBE_DEBUG_LEVEL >= 1)) + PRINTF( + "\nINFO : Process Corner : " + "FFF" + "\n"); +#endif + } + } + + return (part_process_corner); +} + +/** + * @brief Described in fsl_common.h + * @param + * @return + */ +static void lowpower_set_voltage_for_process(lowpower_dcdc_power_profile_enum dcdc_power_profile) +{ + /* Get Sample Process Corner */ + lowpower_process_corner_enum part_process_corner = lowpower_get_part_process_corner(); + + switch (part_process_corner) + { + case PROCESS_CORNER_SSS: + /* Slow Corner */ + { + switch (dcdc_power_profile) + { + case DCDC_POWER_PROFILE_MEDIUM: + /* Medium */ + lowpower_set_system_voltage(VOLTAGE_SSS_MED_MV); + break; + + case DCDC_POWER_PROFILE_HIGH: + /* High */ + lowpower_set_system_voltage(VOLTAGE_SSS_HIG_MV); + break; + + default: + /* DCDC_POWER_PROFILE_LOW */ + lowpower_set_system_voltage(VOLTAGE_SSS_LOW_MV); + break; + } // switch(dcdc_power_profile) + } + break; + + case PROCESS_CORNER_FFF: + /* Fast Corner */ + { + switch (dcdc_power_profile) + { + case DCDC_POWER_PROFILE_MEDIUM: + /* Medium */ + lowpower_set_system_voltage(VOLTAGE_FFF_MED_MV); + break; + + case DCDC_POWER_PROFILE_HIGH: + /* High */ + lowpower_set_system_voltage(VOLTAGE_FFF_HIG_MV); + break; + + default: + /* DCDC_POWER_PROFILE_LOW */ + lowpower_set_system_voltage(VOLTAGE_FFF_LOW_MV); + break; + } // switch(dcdc_power_profile) + } + break; + + default: + /* Nominal (NNN) and all others Process Corners : assume Nominal Corner */ + { + switch (dcdc_power_profile) + { + case DCDC_POWER_PROFILE_MEDIUM: + /* Medium */ + lowpower_set_system_voltage(VOLTAGE_NNN_MED_MV); + break; + + case DCDC_POWER_PROFILE_HIGH: + /* High */ + lowpower_set_system_voltage(VOLTAGE_NNN_HIG_MV); + break; + + default: + /* DCDC_POWER_PROFILE_LOW */ + lowpower_set_system_voltage(VOLTAGE_NNN_LOW_MV); + break; + } // switch(dcdc_power_profile) + break; + } + } // switch(part_process_corner) +} + +/** + * @brief Described in fsl_common.h + * @param + * @return + */ +void POWER_SetVoltageForFreq(uint32_t system_freq_hz) +{ + if (system_freq_hz <= DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ) + { + /* [0 Hz - DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ Hz] */ + lowpower_set_dcdc_power_profile(DCDC_POWER_PROFILE_LOW); /* DCDC VOUT = 1.05 V by default */ + lowpower_set_voltage_for_process(DCDC_POWER_PROFILE_LOW); + } + else + { + if (system_freq_hz <= DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ) + { + /* ]DCDC_POWER_PROFILE_LOW_MAX_FREQ_HZ Hz - DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ Hz] */ + lowpower_set_dcdc_power_profile(DCDC_POWER_PROFILE_MEDIUM); /* DCDC VOUT = 1.15 V by default */ + lowpower_set_voltage_for_process(DCDC_POWER_PROFILE_MEDIUM); + } + else + { + /* > DCDC_POWER_PROFILE_MEDIUM_MAX_FREQ_HZ Hz */ + lowpower_set_dcdc_power_profile(DCDC_POWER_PROFILE_HIGH); /* DCDC VOUT = 1.2 V by default */ + lowpower_set_voltage_for_process(DCDC_POWER_PROFILE_HIGH); + } + } +} + +void POWER_Xtal16mhzCapabankTrim(int32_t pi32_16MfXtalIecLoadpF_x100, + int32_t pi32_16MfXtalPPcbParCappF_x100, + int32_t pi32_16MfXtalNPcbParCappF_x100) +{ + uint32_t u32XOTrimValue; + uint8_t u8IECXinCapCal6pF, u8IECXinCapCal8pF, u8IECXoutCapCal6pF, u8IECXoutCapCal8pF, u8XOSlave; + int32_t iaXin_x4, ibXin, iaXout_x4, ibXout; + int32_t iXOCapInpF_x100, iXOCapOutpF_x100; + uint8_t u8XOCapInCtrl, u8XOCapOutCtrl; + uint32_t u32RegVal; + int32_t i32Tmp; + + /* Enable and set LDO, if not already done */ + POWER_SetXtal16mhzLdo(); + /* Get Cal values from Flash */ + u32XOTrimValue = GET_16MXO_TRIM(); + /* Check validity and apply */ + if ((0UL != (u32XOTrimValue & 1UL)) && (0UL != ((u32XOTrimValue >> 15UL) & 1UL))) + { + /* These fields are 7 bits, unsigned */ + u8IECXinCapCal6pF = (uint8_t)((u32XOTrimValue >> 1UL) & 0x7fUL); + u8IECXinCapCal8pF = (uint8_t)((u32XOTrimValue >> 8UL) & 0x7fUL); + u8IECXoutCapCal6pF = (uint8_t)((u32XOTrimValue >> 16UL) & 0x7fUL); + u8IECXoutCapCal8pF = (uint8_t)((u32XOTrimValue >> 23UL) & 0x7fUL); + /* This field is 1 bit */ + u8XOSlave = (uint8_t)((u32XOTrimValue >> 30UL) & 0x1UL); + /* Linear fit coefficients calculation */ + iaXin_x4 = (int)u8IECXinCapCal8pF - (int)u8IECXinCapCal6pF; + ibXin = (int)u8IECXinCapCal6pF - iaXin_x4 * 3; + iaXout_x4 = (int)u8IECXoutCapCal8pF - (int)u8IECXoutCapCal6pF; + ibXout = (int)u8IECXoutCapCal6pF - iaXout_x4 * 3; + } + else + { + iaXin_x4 = 20; // gain in LSB/pF + ibXin = -9; // offset in LSB + iaXout_x4 = 20; // gain in LSB/pF + ibXout = -13; // offset in LSB + u8XOSlave = 0; + } + /* In & out load cap calculation with derating */ + iXOCapInpF_x100 = 2 * pi32_16MfXtalIecLoadpF_x100 - pi32_16MfXtalNPcbParCappF_x100 + + 39 * ((int32_t)XO_SLAVE_EN - (int32_t)u8XOSlave) - 15; + iXOCapOutpF_x100 = 2 * pi32_16MfXtalIecLoadpF_x100 - pi32_16MfXtalPPcbParCappF_x100 - 21; + /* In & out XO_OSC_CAP_Code_CTRL calculation, with rounding */ + i32Tmp = ((iXOCapInpF_x100 * iaXin_x4 + ibXin * 400) + 200) / 400; + u8XOCapInCtrl = (uint8_t)i32Tmp; + i32Tmp = ((iXOCapOutpF_x100 * iaXout_x4 + ibXout * 400) + 200) / 400; + u8XOCapOutCtrl = (uint8_t)i32Tmp; + /* Read register and clear fields to be written */ + u32RegVal = ANACTRL->XO32M_CTRL; + u32RegVal &= ~(ANACTRL_XO32M_CTRL_OSC_CAP_IN_MASK | ANACTRL_XO32M_CTRL_OSC_CAP_OUT_MASK); + /* Configuration of 32 MHz XO output buffers */ +#if (XO_SLAVE_EN == 0) + u32RegVal &= ~(ANACTRL_XO32M_CTRL_SLAVE_MASK | ANACTRL_XO32M_CTRL_ACBUF_PASS_ENABLE_MASK); +#else + u32RegVal |= ANACTRL_XO32M_CTRL_SLAVE_MASK | ANACTRL_XO32M_CTRL_ACBUF_PASS_ENABLE_MASK; +#endif + /* XO_OSC_CAP_Code_CTRL to XO_OSC_CAP_Code conversion */ + u32RegVal |= (uint32_t)CLOCK_u8OscCapConvert(u8XOCapInCtrl, 13) << ANACTRL_XO32M_CTRL_OSC_CAP_IN_SHIFT; + u32RegVal |= (uint32_t)CLOCK_u8OscCapConvert(u8XOCapOutCtrl, 13) << ANACTRL_XO32M_CTRL_OSC_CAP_OUT_SHIFT; + /* Write back to register */ + ANACTRL->XO32M_CTRL = u32RegVal; +} + +void POWER_Xtal32khzCapabankTrim(int32_t pi32_32kfXtalIecLoadpF_x100, + int32_t pi32_32kfXtalPPcbParCappF_x100, + int32_t pi32_32kfXtalNPcbParCappF_x100) +{ + uint32_t u32XOTrimValue; + uint8_t u8IECXinCapCal6pF, u8IECXinCapCal8pF, u8IECXoutCapCal6pF, u8IECXoutCapCal8pF; + int32_t iaXin_x4, ibXin, iaXout_x4, ibXout; + int32_t iXOCapInpF_x100, iXOCapOutpF_x100; + uint8_t u8XOCapInCtrl, u8XOCapOutCtrl; + uint32_t u32RegVal; + int32_t i32Tmp; + /* Get Cal values from Flash */ + u32XOTrimValue = GET_32KXO_TRIM(); + /* check validity and apply */ + if ((0UL != (u32XOTrimValue & 1UL)) && (0UL != ((u32XOTrimValue >> 15UL) & 1UL))) + { + /* These fields are 7 bits, unsigned */ + u8IECXinCapCal6pF = (uint8_t)((u32XOTrimValue >> 1UL) & 0x7fUL); + u8IECXinCapCal8pF = (uint8_t)((u32XOTrimValue >> 8UL) & 0x7fUL); + u8IECXoutCapCal6pF = (uint8_t)((u32XOTrimValue >> 16UL) & 0x7fUL); + u8IECXoutCapCal8pF = (uint8_t)((u32XOTrimValue >> 23UL) & 0x7fUL); + /* Linear fit coefficients calculation */ + iaXin_x4 = (int)u8IECXinCapCal8pF - (int)u8IECXinCapCal6pF; + ibXin = (int)u8IECXinCapCal6pF - iaXin_x4 * 3; + iaXout_x4 = (int)u8IECXoutCapCal8pF - (int)u8IECXoutCapCal6pF; + ibXout = (int)u8IECXoutCapCal6pF - iaXout_x4 * 3; + } + else + { + iaXin_x4 = 16; // gain in LSB/pF + ibXin = 12; // offset in LSB + iaXout_x4 = 16; // gain in LSB/pF + ibXout = 11; // offset in LSB + } + + /* In & out load cap calculation with derating */ + iXOCapInpF_x100 = 2 * pi32_32kfXtalIecLoadpF_x100 - pi32_32kfXtalNPcbParCappF_x100 - 130; + iXOCapOutpF_x100 = 2 * pi32_32kfXtalIecLoadpF_x100 - pi32_32kfXtalPPcbParCappF_x100 - 41; + + /* In & out XO_OSC_CAP_Code_CTRL calculation, with rounding */ + i32Tmp = ((iXOCapInpF_x100 * iaXin_x4 + ibXin * 400) + 200) / 400; + u8XOCapInCtrl = (uint8_t)i32Tmp; + i32Tmp = ((iXOCapOutpF_x100 * iaXout_x4 + ibXout * 400) + 200) / 400; + u8XOCapOutCtrl = (uint8_t)i32Tmp; + + /* Read register and clear fields to be written */ + u32RegVal = PMC->XTAL32K; + u32RegVal &= ~(PMC_XTAL32K_CAPBANKIN_MASK | PMC_XTAL32K_CAPBANKOUT_MASK); + + /* XO_OSC_CAP_Code_CTRL to XO_OSC_CAP_Code conversion */ + u32RegVal |= (uint32_t)CLOCK_u8OscCapConvert(u8XOCapInCtrl, 23) << PMC_XTAL32K_CAPBANKIN_SHIFT; + u32RegVal |= (uint32_t)CLOCK_u8OscCapConvert(u8XOCapOutCtrl, 23) << PMC_XTAL32K_CAPBANKOUT_SHIFT; + + /* Write back to register */ + PMC->XTAL32K = u32RegVal; +} + +void POWER_SetXtal16mhzLdo(void) +{ + uint32_t temp; + const uint32_t u32Mask = + (ANACTRL_LDO_XO32M_VOUT_MASK | ANACTRL_LDO_XO32M_IBIAS_MASK | ANACTRL_LDO_XO32M_STABMODE_MASK); + + const uint32_t u32Value = + (ANACTRL_LDO_XO32M_VOUT(0x5) | ANACTRL_LDO_XO32M_IBIAS(0x2) | ANACTRL_LDO_XO32M_STABMODE(0x1)); + + /* Enable & set-up XTAL 32 MHz clock LDO */ + temp = ANACTRL->LDO_XO32M; + + if ((temp & u32Mask) != u32Value) + { + temp &= ~u32Mask; + + /* + * Enable the XTAL32M LDO + * Adjust the output voltage level, 0x5 for 1.1V + * Adjust the biasing current, 0x2 value + * Stability configuration, 0x1 default mode + */ + temp |= u32Value; + + ANACTRL->LDO_XO32M = temp; + + /* Delay for LDO to be up */ + // CLOCK_uDelay(20); + } + + /* Enable LDO XO32M */ + PMC->PDRUNCFGCLR0 = PMC_PDRUNCFG0_PDEN_LDOXO32M_MASK; +} + +/*! + * @brief set BOD VBAT level. + * + * @param level BOD detect level + * @param hyst BoD Hysteresis control + * @param enBodVbatReset VBAT brown out detect reset + */ +void POWER_SetBodVbatLevel(power_bod_vbat_level_t level, power_bod_hyst_t hyst, bool enBodVbatReset) +{ + uint32_t pmc_reset_ctrl; + /* BOD VBAT disable reset */ + pmc_reset_ctrl = + PMC->RESETCTRL & (~(PMC_RESETCTRL_BODVBATRESETENA_SECURE_MASK | PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_MASK)); + pmc_reset_ctrl |= (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_SHIFT) | + (0x2UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_SHIFT); + + PMC->RESETCTRL = pmc_reset_ctrl; + + PMC->BODVBAT = (PMC->BODVBAT & (~(PMC_BODVBAT_TRIGLVL_MASK | PMC_BODVBAT_HYST_MASK))) | PMC_BODVBAT_TRIGLVL(level) | + PMC_BODVBAT_HYST(hyst); + + /* BOD VBAT enable reset */ + if ((uint32_t)enBodVbatReset == 1UL) + { + pmc_reset_ctrl &= (~(PMC_RESETCTRL_BODVBATRESETENA_SECURE_MASK | PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_MASK)); + pmc_reset_ctrl |= (0x1UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_SHIFT) | + (0x1UL << PMC_RESETCTRL_BODVBATRESETENA_SECURE_DP_SHIFT); + PMC->RESETCTRL = pmc_reset_ctrl; + } +} + +#if defined(PMC_BODCORE_TRIGLVL_MASK) +/*! + * @brief set BOD core level. + * + * @param level BOD detect level + * @param hyst BoD Hysteresis control + * @param enBodCoreReset core brown out detect reset + */ +void POWER_SetBodCoreLevel(power_bod_core_level_t level, power_bod_hyst_t hyst, bool enBodCoreReset) +{ + uint32_t pmc_reset_ctrl; + /* BOD CORE disable reset */ + pmc_reset_ctrl = + PMC->RESETCTRL & (~(PMC_RESETCTRL_BODCORERESETENA_SECURE_MASK | PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_MASK)); + pmc_reset_ctrl |= + (0x2 << PMC_RESETCTRL_BODCORERESETENA_SECURE_SHIFT) | (0x2 << PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_SHIFT); + + PMC->RESETCTRL = pmc_reset_ctrl; + + PMC->BODCORE = (PMC->BODCORE & (~(PMC_BODCORE_TRIGLVL_MASK | PMC_BODCORE_HYST_MASK))) | PMC_BODCORE_TRIGLVL(level) | + PMC_BODCORE_HYST(hyst); + + /* BOD CORE enable reset */ + if (enBodCoreReset == 1) + { + pmc_reset_ctrl &= (~(PMC_RESETCTRL_BODCORERESETENA_SECURE_MASK | PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_MASK)); + pmc_reset_ctrl |= (0x1 << PMC_RESETCTRL_BODCORERESETENA_SECURE_SHIFT) | + (0x1 << PMC_RESETCTRL_BODCORERESETENA_SECURE_DP_SHIFT); + PMC->RESETCTRL = pmc_reset_ctrl; + } +} +#endif + +/** + * @brief Return some key information related to the device reset causes / wake-up sources, for all power modes. + * @param p_reset_cause : the device reset cause, according to the definition of power_device_reset_cause_t type. + * @param p_boot_mode : the device boot mode, according to the definition of power_device_boot_mode_t type. + * @param p_wakeupio_cause: the wake-up pin sources, according to the definition of register PMC->WAKEIOCAUSE[3:0]. + + * @return Nothing + * + * !!! IMPORTANT ERRATA - IMPORTANT ERRATA - IMPORTANT ERRATA !!! + * !!! valid ONLY for LPC55S69 (not for LPC55S16 and LPC55S06) !!! + * !!! when FALLING EDGE DETECTION is enabled on wake-up pins: !!! + * - 1. p_wakeupio_cause is NOT ACCURATE + * - 2. Spurious kRESET_CAUSE_DPDRESET_WAKEUPIO* event is reported when + * several wake-up sources are enabled during DEEP-POWER-DOWN + * (like enabling wake-up on RTC and Falling edge wake-up pins) + * + */ +void POWER_GetWakeUpCause(power_device_reset_cause_t *p_reset_cause, + power_device_boot_mode_t *p_boot_mode, + uint32_t *p_wakeupio_cause) +{ + uint32_t reset_cause_reg; + uint32_t boot_mode_reg; + +#if (defined(LPC55S06_SERIES) || defined(LPC55S04_SERIES) || defined(LPC5506_SERIES) || defined(LPC5504_SERIES) || \ + defined(LPC5502_SERIES) || defined(LPC55S16_SERIES) || defined(LPC55S14_SERIES) || defined(LPC5516_SERIES) || \ + defined(LPC5514_SERIES) || defined(LPC5512_SERIES)) + reset_cause_reg = (PMC->AOREG1) & 0x3FF0UL; +#else /* LPC55S69/28 */ + reset_cause_reg = (PMC->AOREG1) & 0x1FF0UL; +#endif + + /* + * Prioritize interrupts source with respect to their critical level + */ +#if (defined(LPC55S06_SERIES) || defined(LPC55S04_SERIES) || defined(LPC5506_SERIES) || defined(LPC5504_SERIES) || \ + defined(LPC5502_SERIES) || defined(LPC55S16_SERIES) || defined(LPC55S14_SERIES) || defined(LPC5516_SERIES) || \ + defined(LPC5514_SERIES) || defined(LPC5512_SERIES)) + if (0UL != (reset_cause_reg & PMC_AOREG1_CDOGRESET_MASK)) + { /* Code Watchdog Reset */ + *p_reset_cause = kRESET_CAUSE_CDOGRESET; + *p_boot_mode = kBOOT_MODE_POWER_UP; + *p_wakeupio_cause = 0; /* Device has not been waked-up by any wake-up pins */ + } + else +#endif + { + if (0UL != (reset_cause_reg & PMC_AOREG1_WDTRESET_MASK)) + { /* Watchdog Timer Reset */ + *p_reset_cause = kRESET_CAUSE_WDTRESET; + *p_boot_mode = kBOOT_MODE_POWER_UP; + *p_wakeupio_cause = 0; /* Device has not been waked-up by any wake-up pins */ + } + else + { + if (0UL != (reset_cause_reg & PMC_AOREG1_SYSTEMRESET_MASK)) + { /* ARM System Reset */ + *p_reset_cause = kRESET_CAUSE_ARMSYSTEMRESET; + *p_boot_mode = kBOOT_MODE_POWER_UP; + *p_wakeupio_cause = 0; /* Device has not been waked-up by any wake-up pins */ + } + else + { + boot_mode_reg = (PMC->STATUS & PMC_STATUS_BOOTMODE_MASK) >> PMC_STATUS_BOOTMODE_SHIFT; + + if (boot_mode_reg == 0UL) /* POWER-UP: Power On Reset, Pin reset, Brown Out Detectors, Software Reset */ + { + *p_boot_mode = kBOOT_MODE_POWER_UP; /* All non wake-up from a Low Power mode */ + *p_wakeupio_cause = 0; /* Device has not been waked-up by any wake-up pins */ + + /* + * Prioritise Reset causes, starting from the strongest (Power On Reset) + */ + if (0UL != (reset_cause_reg & PMC_AOREG1_POR_MASK)) + { /* Power On Reset */ + *p_reset_cause = kRESET_CAUSE_POR; + } + else + { + if (0UL != (reset_cause_reg & PMC_AOREG1_BODRESET_MASK)) + { /* Brown-out Detector reset (either BODVBAT or BODCORE) */ + *p_reset_cause = kRESET_CAUSE_BODRESET; + } + else + { + if (0UL != (reset_cause_reg & PMC_AOREG1_PADRESET_MASK)) + { /* Hardware Pin Reset */ + *p_reset_cause = kRESET_CAUSE_PADRESET; + } + else + { + if (0UL != (reset_cause_reg & PMC_AOREG1_SWRRESET_MASK)) + { /* Software triggered Reset */ + *p_reset_cause = kRESET_CAUSE_SWRRESET; + } + else + { /* Unknown Reset Cause */ + *p_reset_cause = kRESET_CAUSE_NOT_DETERMINISTIC; + } + } + } + } + +#if (defined(LPC55S06_SERIES) || defined(LPC55S04_SERIES) || defined(LPC5506_SERIES) || defined(LPC5504_SERIES) || \ + defined(LPC5502_SERIES) || defined(LPC55S16_SERIES) || defined(LPC55S14_SERIES) || defined(LPC5516_SERIES) || \ + defined(LPC5514_SERIES) || defined(LPC5512_SERIES)) + /* Transfer the control of the 4 wake-up pins to IOCON (instead of the Power Management Controller + */ + PMC->WAKEUPIOCTRL = PMC->WAKEUPIOCTRL & (~PMC_WAKEUPIOCTRL_WAKEUPIO_ENABLE_CTRL_MASK); +#endif + } + else /* DEEP-SLEEP, POWER-DOWN and DEEP-POWER-DOWN */ + { + /* + * 1- First, save wakeup_io_cause register ... + */ + *p_wakeupio_cause = PMC->WAKEIOCAUSE; + + if (boot_mode_reg == 3UL) /* DEEP-POWER-DOWN */ + { + *p_boot_mode = kBOOT_MODE_LP_DEEP_POWER_DOWN; + + switch (((reset_cause_reg >> PMC_AOREG1_DPDRESET_WAKEUPIO_SHIFT) & 0x7UL)) + { + case 1: + *p_reset_cause = kRESET_CAUSE_DPDRESET_WAKEUPIO; + break; + case 2: + *p_reset_cause = kRESET_CAUSE_DPDRESET_RTC; + break; + case 3: + *p_reset_cause = kRESET_CAUSE_DPDRESET_WAKEUPIO_RTC; + break; + case 4: + *p_reset_cause = kRESET_CAUSE_DPDRESET_OSTIMER; + break; + case 5: + *p_reset_cause = kRESET_CAUSE_DPDRESET_WAKEUPIO_OSTIMER; + break; + case 6: + *p_reset_cause = kRESET_CAUSE_DPDRESET_RTC_OSTIMER; + break; + case 7: + *p_reset_cause = kRESET_CAUSE_DPDRESET_WAKEUPIO_RTC_OSTIMER; + break; + default: + /* Unknown Reset Cause */ + *p_reset_cause = kRESET_CAUSE_NOT_DETERMINISTIC; + break; + } + +#if (defined(LPC55S06_SERIES) || defined(LPC55S04_SERIES) || defined(LPC5506_SERIES) || defined(LPC5504_SERIES) || \ + defined(LPC5502_SERIES) || defined(LPC55S16_SERIES) || defined(LPC55S14_SERIES) || defined(LPC5516_SERIES) || \ + defined(LPC5514_SERIES) || defined(LPC5512_SERIES)) + /* + * 2- Next, transfer the control of the 4 wake-up pins + * to IOCON (instead of the Power Management Controller) + */ + PMC->WAKEUPIOCTRL = PMC->WAKEUPIOCTRL & (~PMC_WAKEUPIOCTRL_WAKEUPIO_ENABLE_CTRL_MASK); +#endif + } + else /* DEEP-SLEEP and POWER-DOWN */ + { + *p_reset_cause = kRESET_CAUSE_NOT_RELEVANT; + + /* + * The control of the 4 wake-up pins is already in IOCON, + * so there is nothing special to do. + */ + + if (boot_mode_reg == 1UL) /* DEEP-SLEEP */ + { + *p_boot_mode = kBOOT_MODE_LP_DEEP_SLEEP; + } + else /* POWER-DOWN */ + { + *p_boot_mode = kBOOT_MODE_LP_POWER_DOWN; + + } /* if ( boot_mode_reg == 1 ) DEEP-SLEEP */ + + } /* if ( boot_mode == 3 ) DEEP-POWER-DOWN */ + + } /* if ( boot_mode == 0 ) POWER-UP */ + + } /* if ( reset_cause_reg & PMC_AOREG1_CDOGRESET_MASK ) */ + + } /* if ( reset_cause_reg & PMC_AOREG1_WDTRESET_MASK ) */ + + } /* if ( reset_cause_reg & PMC_AOREG1_SYSTEMRESET_MASK ) */ +} diff --git a/drivers/fsl_power.h b/drivers/fsl_power.h new file mode 100644 index 0000000..691921f --- /dev/null +++ b/drivers/fsl_power.h @@ -0,0 +1,598 @@ +/* + * Copyright 2017, NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_POWER_H_ +#define _FSL_POWER_H_ + +#include "fsl_common.h" +#include "fsl_device_registers.h" +#include + +/*! + * @addtogroup power + * @{ + */ +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +/*! @brief power driver version 1.0.0. */ +#define FSL_POWER_DRIVER_VERSION (MAKE_VERSION(1, 0, 0)) +/*@}*/ + +/* Power mode configuration API parameter */ +typedef enum _power_mode_config +{ + kPmu_Sleep = 0U, + kPmu_Deep_Sleep = 1U, + kPmu_PowerDown = 2U, + kPmu_Deep_PowerDown = 3U, +} power_mode_cfg_t; + +/** + * @brief Analog components power modes control during low power modes + */ +typedef enum pd_bits +{ + kPDRUNCFG_PD_DCDC = (1UL << 0), + kPDRUNCFG_PD_BIAS = (1UL << 1), + kPDRUNCFG_PD_BODCORE = (1UL << 2), + kPDRUNCFG_PD_BODVBAT = (1UL << 3), + kPDRUNCFG_PD_FRO1M = (1UL << 4), + kPDRUNCFG_PD_FRO192M = (1UL << 5), + kPDRUNCFG_PD_FRO32K = (1UL << 6), + kPDRUNCFG_PD_XTAL32K = (1UL << 7), + kPDRUNCFG_PD_XTAL32M = (1UL << 8), + kPDRUNCFG_PD_PLL0 = (1UL << 9), + kPDRUNCFG_PD_PLL1 = (1UL << 10), + kPDRUNCFG_PD_USB0_PHY = (1UL << 11), + kPDRUNCFG_PD_USB1_PHY = (1UL << 12), + kPDRUNCFG_PD_COMP = (1UL << 13), + kPDRUNCFG_PD_TEMPSENS = (1UL << 14), + kPDRUNCFG_PD_GPADC = (1UL << 15), + kPDRUNCFG_PD_LDOMEM = (1UL << 16), + kPDRUNCFG_PD_LDODEEPSLEEP = (1UL << 17), + kPDRUNCFG_PD_LDOUSBHS = (1UL << 18), + kPDRUNCFG_PD_LDOGPADC = (1UL << 19), + kPDRUNCFG_PD_LDOXO32M = (1UL << 20), + kPDRUNCFG_PD_LDOFLASHNV = (1UL << 21), + kPDRUNCFG_PD_RNG = (1UL << 22), + kPDRUNCFG_PD_PLL0_SSCG = (1UL << 23), + kPDRUNCFG_PD_ROM = (1UL << 24), + /* + This enum member has no practical meaning,it is used to avoid MISRA issue, + user should not trying to use it. + */ + kPDRUNCFG_ForceUnsigned = 0x80000000U, +} pd_bit_t; + +/** + * @brief BOD VBAT level + */ +typedef enum _power_bod_vbat_level +{ + kPOWER_BodVbatLevel1000mv = 0, /*!< Brown out detector VBAT level 1V */ + kPOWER_BodVbatLevel1100mv = 1, /*!< Brown out detector VBAT level 1.1V */ + kPOWER_BodVbatLevel1200mv = 2, /*!< Brown out detector VBAT level 1.2V */ + kPOWER_BodVbatLevel1300mv = 3, /*!< Brown out detector VBAT level 1.3V */ + kPOWER_BodVbatLevel1400mv = 4, /*!< Brown out detector VBAT level 1.4V */ + kPOWER_BodVbatLevel1500mv = 5, /*!< Brown out detector VBAT level 1.5V */ + kPOWER_BodVbatLevel1600mv = 6, /*!< Brown out detector VBAT level 1.6V */ + kPOWER_BodVbatLevel1650mv = 7, /*!< Brown out detector VBAT level 1.65V */ + kPOWER_BodVbatLevel1700mv = 8, /*!< Brown out detector VBAT level 1.7V */ + kPOWER_BodVbatLevel1750mv = 9, /*!< Brown out detector VBAT level 1.75V */ + kPOWER_BodVbatLevel1800mv = 10, /*!< Brown out detector VBAT level 1.8V */ + kPOWER_BodVbatLevel1900mv = 11, /*!< Brown out detector VBAT level 1.9V */ + kPOWER_BodVbatLevel2000mv = 12, /*!< Brown out detector VBAT level 2V */ + kPOWER_BodVbatLevel2100mv = 13, /*!< Brown out detector VBAT level 2.1V */ + kPOWER_BodVbatLevel2200mv = 14, /*!< Brown out detector VBAT level 2.2V */ + kPOWER_BodVbatLevel2300mv = 15, /*!< Brown out detector VBAT level 2.3V */ + kPOWER_BodVbatLevel2400mv = 16, /*!< Brown out detector VBAT level 2.4V */ + kPOWER_BodVbatLevel2500mv = 17, /*!< Brown out detector VBAT level 2.5V */ + kPOWER_BodVbatLevel2600mv = 18, /*!< Brown out detector VBAT level 2.6V */ + kPOWER_BodVbatLevel2700mv = 19, /*!< Brown out detector VBAT level 2.7V */ + kPOWER_BodVbatLevel2806mv = 20, /*!< Brown out detector VBAT level 2.806V */ + kPOWER_BodVbatLevel2900mv = 21, /*!< Brown out detector VBAT level 2.9V */ + kPOWER_BodVbatLevel3000mv = 22, /*!< Brown out detector VBAT level 3.0V */ + kPOWER_BodVbatLevel3100mv = 23, /*!< Brown out detector VBAT level 3.1V */ + kPOWER_BodVbatLevel3200mv = 24, /*!< Brown out detector VBAT level 3.2V */ + kPOWER_BodVbatLevel3300mv = 25, /*!< Brown out detector VBAT level 3.3V */ +} power_bod_vbat_level_t; + +/** + * @brief BOD Hysteresis control + */ +typedef enum _power_bod_hyst +{ + kPOWER_BodHystLevel25mv = 0U, /*!< BOD Hysteresis control level 25mv */ + kPOWER_BodHystLevel50mv = 1U, /*!< BOD Hysteresis control level 50mv */ + kPOWER_BodHystLevel75mv = 2U, /*!< BOD Hysteresis control level 75mv */ + kPOWER_BodHystLevel100mv = 3U, /*!< BOD Hysteresis control level 100mv */ +} power_bod_hyst_t; +/** + * @brief BOD core level + */ +typedef enum _power_bod_core_level +{ + kPOWER_BodCoreLevel600mv = 0, /*!< Brown out detector core level 600mV */ + kPOWER_BodCoreLevel650mv = 1, /*!< Brown out detector core level 650mV */ + kPOWER_BodCoreLevel700mv = 2, /*!< Brown out detector core level 700mV */ + kPOWER_BodCoreLevel750mv = 3, /*!< Brown out detector core level 750mV */ + kPOWER_BodCoreLevel800mv = 4, /*!< Brown out detector core level 800mV */ + kPOWER_BodCoreLevel850mv = 5, /*!< Brown out detector core level 850mV */ + kPOWER_BodCoreLevel900mv = 6, /*!< Brown out detector core level 900mV */ + kPOWER_BodCoreLevel950mv = 7, /*!< Brown out detector core level 950mV */ +} power_bod_core_level_t; + +/** + * @brief Device Reset Causes + */ +typedef enum _power_device_reset_cause +{ + kRESET_CAUSE_POR = 0UL, /*!< Power On Reset */ + kRESET_CAUSE_PADRESET = 1UL, /*!< Hardware Pin Reset */ + kRESET_CAUSE_BODRESET = 2UL, /*!< Brown-out Detector reset (either BODVBAT or BODCORE) */ + kRESET_CAUSE_ARMSYSTEMRESET = 3UL, /*!< ARM System Reset */ + kRESET_CAUSE_WDTRESET = 4UL, /*!< Watchdog Timer Reset */ + kRESET_CAUSE_SWRRESET = 5UL, /*!< Software Reset */ + kRESET_CAUSE_CDOGRESET = 6UL, /*!< Code Watchdog Reset */ + /* Reset causes in DEEP-POWER-DOWN low power mode */ + kRESET_CAUSE_DPDRESET_WAKEUPIO = 7UL, /*!< Any of the 4 wake-up pins */ + kRESET_CAUSE_DPDRESET_RTC = 8UL, /*!< Real Time Counter (RTC) */ + kRESET_CAUSE_DPDRESET_OSTIMER = 9UL, /*!< OS Event Timer (OSTIMER) */ + kRESET_CAUSE_DPDRESET_WAKEUPIO_RTC = 10UL, /*!< Any of the 4 wake-up pins and RTC (it is not possible to distinguish + which of these 2 events occured first) */ + kRESET_CAUSE_DPDRESET_WAKEUPIO_OSTIMER = 11UL, /*!< Any of the 4 wake-up pins and OSTIMER (it is not possible to + distinguish which of these 2 events occured first) */ + kRESET_CAUSE_DPDRESET_RTC_OSTIMER = 12UL, /*!< Real Time Counter or OS Event Timer (it is not possible to + distinguish which of these 2 events occured first) */ + kRESET_CAUSE_DPDRESET_WAKEUPIO_RTC_OSTIMER = 13UL, /*!< Any of the 4 wake-up pins (it is not possible to distinguish + which of these 3 events occured first) */ + /* Miscallenous */ + kRESET_CAUSE_NOT_RELEVANT = + 14UL, /*!< No reset cause (for example, this code is used when waking up from DEEP-SLEEP low power mode) */ + kRESET_CAUSE_NOT_DETERMINISTIC = 15UL, /*!< Unknown Reset Cause. Should be treated like "Hardware Pin Reset" from an + application point of view. */ +} power_device_reset_cause_t; + +/** + * @brief Device Boot Modes + */ +typedef enum _power_device_boot_mode +{ + kBOOT_MODE_POWER_UP = + 0UL, /*!< All non Low Power Mode wake up (Power On Reset, Pin Reset, BoD Reset, ARM System Reset ... ) */ + kBOOT_MODE_LP_DEEP_SLEEP = 1UL, /*!< Wake up from DEEP-SLEEP Low Power mode */ + kBOOT_MODE_LP_POWER_DOWN = 2UL, /*!< Wake up from POWER-DOWN Low Power mode */ + kBOOT_MODE_LP_DEEP_POWER_DOWN = 4UL, /*!< Wake up from DEEP-POWER-DOWN Low Power mode */ +} power_device_boot_mode_t; + +/** + * @brief SRAM instances retention control during low power modes + */ +#define LOWPOWER_SRAMRETCTRL_RETEN_RAMX0 \ + (1UL << 0) /*!< Enable SRAMX_0 retention when entering in Low power modes */ +#define LOWPOWER_SRAMRETCTRL_RETEN_RAMX1 \ + (1UL << 1) /*!< Enable SRAMX_1 retention when entering in Low power modes */ +#define LOWPOWER_SRAMRETCTRL_RETEN_RAMX2 \ + (1UL << 2) /*!< Enable SRAMX_2 retention when entering in Low power modes */ +#define LOWPOWER_SRAMRETCTRL_RETEN_RAMX3 \ + (1UL << 3) /*!< Enable SRAMX_3 retention when entering in Low power modes */ +#define LOWPOWER_SRAMRETCTRL_RETEN_RAM00 \ + (1UL << 4) /*!< Enable SRAM0_0 retention when entering in Low power modes */ +#define LOWPOWER_SRAMRETCTRL_RETEN_RAM10 \ + (1UL << 6) /*!< Enable SRAM1_0 retention when entering in Low power modes */ +#define LOWPOWER_SRAMRETCTRL_RETEN_RAM20 \ + (1UL << 7) /*!< Enable SRAM2_0 retention when entering in Low power modes */ +#define LOWPOWER_SRAMRETCTRL_RETEN_RAM_USB_HS \ + (1UL << 14) /*!< Enable SRAM USB HS retention when entering in Low power modes */ + +/** + * @brief Low Power Modes Wake up sources + */ +#define WAKEUP_SYS (1ULL << 0) /*!< [SLEEP, DEEP SLEEP ] */ /* WWDT0_IRQ and BOD_IRQ*/ +#define WAKEUP_SDMA0 (1ULL << 1) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_GPIO_GLOBALINT0 (1ULL << 2) /*!< [SLEEP, DEEP SLEEP, POWER DOWN ] */ +#define WAKEUP_GPIO_GLOBALINT1 (1ULL << 3) /*!< [SLEEP, DEEP SLEEP, POWER DOWN ] */ +#define WAKEUP_GPIO_INT0_0 (1ULL << 4) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_GPIO_INT0_1 (1ULL << 5) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_GPIO_INT0_2 (1ULL << 6) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_GPIO_INT0_3 (1ULL << 7) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_UTICK (1ULL << 8) /*!< [SLEEP, ] */ +#define WAKEUP_MRT (1ULL << 9) /*!< [SLEEP, ] */ +#define WAKEUP_CTIMER0 (1ULL << 10) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_CTIMER1 (1ULL << 11) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_SCT (1ULL << 12) /*!< [SLEEP, ] */ +#define WAKEUP_CTIMER3 (1ULL << 13) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_FLEXCOMM0 (1ULL << 14) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_FLEXCOMM1 (1ULL << 15) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_FLEXCOMM2 (1ULL << 16) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_FLEXCOMM3 (1ULL << 17) /*!< [SLEEP, DEEP SLEEP, POWER DOWN ] */ +#define WAKEUP_FLEXCOMM4 (1ULL << 18) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_FLEXCOMM5 (1ULL << 19) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_FLEXCOMM6 (1ULL << 20) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_FLEXCOMM7 (1ULL << 21) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_ADC (1ULL << 22) /*!< [SLEEP, ] */ +// reserved (1ULL << 23) +#define WAKEUP_ACMP (1ULL << 24) /*!< [SLEEP, DEEP SLEEP, POWER DOWN ] */ +// reserved (1ULL << 25) +// reserved (1ULL << 26) +#define WAKEUP_USB0_NEEDCLK (1ULL << 27) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_USB0 (1ULL << 28) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_RTC_LITE_ALARM_WAKEUP (1ULL << 29) /*!< [SLEEP, DEEP SLEEP, POWER DOWN, DEEP POWER DOWN] */ +// reserved (1ULL << 30) +// reserved (1ULL << 31) +#define WAKEUP_GPIO_INT0_4 (1ULL << 32) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_GPIO_INT0_5 (1ULL << 33) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_GPIO_INT0_6 (1ULL << 34) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_GPIO_INT0_7 (1ULL << 35) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_CTIMER2 (1ULL << 36) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_CTIMER4 (1ULL << 37) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_OS_EVENT_TIMER (1ULL << 38) /*!< [SLEEP, DEEP SLEEP, POWER DOWN, DEEP POWER DOWN] */ +// reserved (1ULL << 39) +// reserved (1ULL << 40) +// reserved (1ULL << 41) +// reserved (1ULL << 42) +#define CAN0_INT0 (1ULL << 43) /*!< [SLEEP, ] */ +#define CAN1_INT0 (1ULL << 44) /*!< [SLEEP, ] */ +// reserved (1ULL << 45) +// reserved (1ULL << 46) +#define WAKEUP_USB1 (1ULL << 47) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_USB1_NEEDCLK (1ULL << 48) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_SEC_HYPERVISOR_CALL (1ULL << 49) /*!< [SLEEP, ] */ +#define WAKEUP_SEC_GPIO_INT0_0 (1ULL << 50) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_SEC_GPIO_INT0_1 (1ULL << 51) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_PLU (1ULL << 52) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_SEC_VIO (1ULL << 53) +#define WAKEUP_SHA (1ULL << 54) /*!< [SLEEP, ] */ +#define WAKEUP_CASPER (1ULL << 55) /*!< [SLEEP, ] */ +#define WAKEUP_PUF (1ULL << 56) /*!< [SLEEP, ] */ +// reserved (1ULL << 57) +#define WAKEUP_SDMA1 (1ULL << 58) /*!< [SLEEP, DEEP SLEEP ] */ +#define WAKEUP_LSPI_HS (1ULL << 59) /*!< [SLEEP, DEEP SLEEP ] */ +//#define CDOG (1ULL << 60) !< [SLEEP, ] +// reserved (1ULL << 61) +// reserved (1ULL << 62) +#define WAKEUP_ALLWAKEUPIOS (1ULL << 63) /*!< [ , DEEP POWER DOWN] */ + +/** + * @brief Sleep Postpone + */ +#define LOWPOWER_HWWAKE_FORCED (1UL << 0) /*!< Force peripheral clocking to stay on during deep-sleep mode. */ +#define LOWPOWER_HWWAKE_PERIPHERALS \ + (1UL << 1) /*!< Wake for Flexcomms. Any Flexcomm FIFO reaching the level specified by its own TXLVL will cause \ + peripheral clocking to wake up temporarily while the related status is asserted */ +#define LOWPOWER_HWWAKE_SDMA0 \ + (1UL << 3) /*!< Wake for DMA0. DMA0 being busy will cause peripheral clocking to remain running until DMA \ + completes. Used in conjonction with LOWPOWER_HWWAKE_PERIPHERALS */ +#define LOWPOWER_HWWAKE_SDMA1 \ + (1UL << 5) /*!< Wake for DMA1. DMA0 being busy will cause peripheral clocking to remain running until DMA \ + completes. Used in conjonction with LOWPOWER_HWWAKE_PERIPHERALS */ +#define LOWPOWER_HWWAKE_ENABLE_FRO192M \ + (1UL << 31) /*!< Need to be set if FRO192M is disable - via PDCTRL0 - in Deep Sleep mode and any of \ + LOWPOWER_HWWAKE_PERIPHERALS, LOWPOWER_HWWAKE_SDMA0 or LOWPOWER_HWWAKE_SDMA1 is set */ + +#define LOWPOWER_CPURETCTRL_ENA_DISABLE 0 /*!< In POWER DOWN mode, CPU Retention is disabled */ +#define LOWPOWER_CPURETCTRL_ENA_ENABLE 1 /*!< In POWER DOWN mode, CPU Retention is enabled */ +/** + * @brief Wake up I/O sources + */ +#define LOWPOWER_WAKEUPIOSRC_PIO0_INDEX 0 /*!< Pin P1( 1) */ +#define LOWPOWER_WAKEUPIOSRC_PIO1_INDEX 2 /*!< Pin P0(28) */ +#define LOWPOWER_WAKEUPIOSRC_PIO2_INDEX 4 /*!< Pin P1(18) */ +#define LOWPOWER_WAKEUPIOSRC_PIO3_INDEX 6 /*!< Pin P1(30) */ + +#define LOWPOWER_WAKEUPIOSRC_DISABLE 0 /*!< Wake up is disable */ +#define LOWPOWER_WAKEUPIOSRC_RISING 1 /*!< Wake up on rising edge */ +#define LOWPOWER_WAKEUPIOSRC_FALLING 2 /*!< Wake up on falling edge */ +#define LOWPOWER_WAKEUPIOSRC_RISING_FALLING 3 /*!< Wake up on both rising or falling edges */ + +#define LOWPOWER_WAKEUPIOSRC_PIO0MODE_INDEX 12 /*!< Pin P1( 1) */ +#define LOWPOWER_WAKEUPIOSRC_PIO1MODE_INDEX 14 /*!< Pin P0(28) */ +#define LOWPOWER_WAKEUPIOSRC_PIO2MODE_INDEX 16 /*!< Pin P1(18) */ +#define LOWPOWER_WAKEUPIOSRC_PIO3MODE_INDEX 18 /*!< Pin P1(30) */ + +#define LOWPOWER_WAKEUPIOSRC_IO_MODE_PLAIN 0 /*!< Wake up Pad is plain input */ +#define LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLDOWN 1 /*!< Wake up Pad is pull-down */ +#define LOWPOWER_WAKEUPIOSRC_IO_MODE_PULLUP 2 /*!< Wake up Pad is pull-up */ +#define LOWPOWER_WAKEUPIOSRC_IO_MODE_REPEATER 3 /*!< Wake up Pad is in repeater */ + +#define LOWPOWER_WAKEUPIO_PIO0_PULLUPDOWN_INDEX 8 /*!< Wake-up I/O 0 pull-up/down configuration index */ +#define LOWPOWER_WAKEUPIO_PIO1_PULLUPDOWN_INDEX 9 /*!< Wake-up I/O 1 pull-up/down configuration index */ +#define LOWPOWER_WAKEUPIO_PIO2_PULLUPDOWN_INDEX 10 /*!< Wake-up I/O 2 pull-up/down configuration index */ +#define LOWPOWER_WAKEUPIO_PIO3_PULLUPDOWN_INDEX 11 /*!< Wake-up I/O 3 pull-up/down configuration index */ + +#define LOWPOWER_WAKEUPIO_PIO0_PULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO0_PULLUPDOWN_INDEX) /*!< Wake-up I/O 0 pull-up/down mask */ +#define LOWPOWER_WAKEUPIO_PIO1_PULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO1_PULLUPDOWN_INDEX) /*!< Wake-up I/O 1 pull-up/down mask */ +#define LOWPOWER_WAKEUPIO_PIO2_PULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO2_PULLUPDOWN_INDEX) /*!< Wake-up I/O 2 pull-up/down mask */ +#define LOWPOWER_WAKEUPIO_PIO3_PULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO3_PULLUPDOWN_INDEX) /*!< Wake-up I/O 3 pull-up/down mask */ + +#define LOWPOWER_WAKEUPIO_PULLDOWN 0 /*!< Select pull-down */ +#define LOWPOWER_WAKEUPIO_PULLUP 1 /*!< Select pull-up */ + +#define LOWPOWER_WAKEUPIO_PIO0_DISABLEPULLUPDOWN_INDEX \ + 12 /*!< Wake-up I/O 0 pull-up/down disable/enable control index */ +#define LOWPOWER_WAKEUPIO_PIO1_DISABLEPULLUPDOWN_INDEX \ + 13 /*!< Wake-up I/O 1 pull-up/down disable/enable control index */ +#define LOWPOWER_WAKEUPIO_PIO2_DISABLEPULLUPDOWN_INDEX \ + 14 /*!< Wake-up I/O 2 pull-up/down disable/enable control index */ +#define LOWPOWER_WAKEUPIO_PIO3_DISABLEPULLUPDOWN_INDEX \ + 15 /*!< Wake-up I/O 3 pull-up/down disable/enable control index */ +#define LOWPOWER_WAKEUPIO_PIO0_DISABLEPULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO0_DISABLEPULLUPDOWN_INDEX) /*!< Wake-up I/O 0 pull-up/down disable/enable mask */ +#define LOWPOWER_WAKEUPIO_PIO1_DISABLEPULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO1_DISABLEPULLUPDOWN_INDEX) /*!< Wake-up I/O 1 pull-up/down disable/enable mask */ +#define LOWPOWER_WAKEUPIO_PIO2_DISABLEPULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO2_DISABLEPULLUPDOWN_INDEX) /*!< Wake-up I/O 2 pull-up/down disable/enable mask */ +#define LOWPOWER_WAKEUPIO_PIO3_DISABLEPULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO3_DISABLEPULLUPDOWN_INDEX) /*!< Wake-up I/O 3 pull-up/down disable/enable mask */ + +#define LOWPOWER_WAKEUPIO_PIO0_USEEXTERNALPULLUPDOWN_INDEX \ + (16) /*!< Wake-up I/O 0 use external pull-up/down disable/enable control index*/ +#define LOWPOWER_WAKEUPIO_PIO1_USEEXTERNALPULLUPDOWN_INDEX \ + (17) /*!< Wake-up I/O 1 use external pull-up/down disable/enable control index */ +#define LOWPOWER_WAKEUPIO_PIO2_USEEXTERNALPULLUPDOWN_INDEX \ + (18) /*!< Wake-up I/O 2 use external pull-up/down disable/enable control index */ +#define LOWPOWER_WAKEUPIO_PIO3_USEEXTERNALPULLUPDOWN_INDEX \ + (19) /*!< Wake-up I/O 3 use external pull-up/down disable/enable control index */ +#define LOWPOWER_WAKEUPIO_PIO0_USEEXTERNALPULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO0_USEEXTERNALPULLUPDOWN_INDEX) /*!< Wake-up I/O 0 use external pull-up/down \ + disable/enable mask, 0: disable, 1: enable */ +#define LOWPOWER_WAKEUPIO_PIO1_USEEXTERNALPULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO1_USEEXTERNALPULLUPDOWN_INDEX) /*!< Wake-up I/O 1 use external pull-up/down \ + disable/enable mask, 0: disable, 1: enable */ +#define LOWPOWER_WAKEUPIO_PIO2_USEEXTERNALPULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO2_USEEXTERNALPULLUPDOWN_INDEX) /*!< Wake-up I/O 2 use external pull-up/down \ + disable/enable mask, 0: disable, 1: enable */ +#define LOWPOWER_WAKEUPIO_PIO3_USEEXTERNALPULLUPDOWN_MASK \ + (1UL << LOWPOWER_WAKEUPIO_PIO3_USEEXTERNALPULLUPDOWN_INDEX) /*!< Wake-up I/O 3 use external pull-up/down \ + disable/enable mask, 0: disable, 1: enable */ + +#ifdef __cplusplus +extern "C" { +#endif +/******************************************************************************* + * API + ******************************************************************************/ + +/*! + * @brief API to enable PDRUNCFG bit in the Syscon. Note that enabling the bit powers down the peripheral + * + * @param en peripheral for which to enable the PDRUNCFG bit + * @return none + */ +static inline void POWER_EnablePD(pd_bit_t en) +{ + /* PDRUNCFGSET */ + PMC->PDRUNCFGSET0 = (uint32_t)en; +} + +/*! + * @brief API to disable PDRUNCFG bit in the Syscon. Note that disabling the bit powers up the peripheral + * + * @param en peripheral for which to disable the PDRUNCFG bit + * @return none + */ +static inline void POWER_DisablePD(pd_bit_t en) +{ + /* PDRUNCFGCLR */ + PMC->PDRUNCFGCLR0 = (uint32_t)en; +} + +/*! + * @brief set BOD VBAT level. + * + * @param level BOD detect level + * @param hyst BoD Hysteresis control + * @param enBodVbatReset VBAT brown out detect reset + */ +void POWER_SetBodVbatLevel(power_bod_vbat_level_t level, power_bod_hyst_t hyst, bool enBodVbatReset); + +#if defined(PMC_BODCORE_TRIGLVL_MASK) +/*! + * @brief set BOD core level. + * + * @param level BOD detect level + * @param hyst BoD Hysteresis control + * @param enBodCoreReset core brown out detect reset + */ +void POWER_SetBodCoreLevel(power_bod_core_level_t level, power_bod_hyst_t hyst, bool enBodCoreReset); +#endif + +/*! + * @brief API to enable deep sleep bit in the ARM Core. + * + * @return none + */ +static inline void POWER_EnableDeepSleep(void) +{ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; +} + +/*! + * @brief API to disable deep sleep bit in the ARM Core. + * + * @return none + */ +static inline void POWER_DisableDeepSleep(void) +{ + SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; +} + +/** + * @brief Shut off the Flash and execute the _WFI(), then power up the Flash after wake-up event + * This MUST BE EXECUTED outside the Flash: + * either from ROM or from SRAM. The rest could stay in Flash. But, for consistency, it is + * preferable to have all functions defined in this file implemented in ROM. + * + * @return Nothing + */ +void POWER_CycleCpuAndFlash(void); + +/** + * @brief Configures and enters in DEEP-SLEEP low power mode + * @param exclude_from_pd: + * @param sram_retention_ctrl: + * @param wakeup_interrupts: + * @param hardware_wake_ctrl: + + * @return Nothing + * + * !!! IMPORTANT NOTES : + 0 - CPU0 & System CLock frequency is switched to FRO12MHz and is NOT restored back by the API. + * 1 - CPU0 Interrupt Enable registers (NVIC->ISER) are modified by this function. They are restored back in + case of CPU retention or if POWERDOWN is not taken (for instance because an interrupt is pending). + * 2 - The Non Maskable Interrupt (NMI) is disabled and its configuration before calling this function will be + restored back if POWERDOWN is not taken (for instance because an RTC or OSTIMER interrupt is pending). + * 3 - The HARD FAULT handler should execute from SRAM. (The Hard fault handler should initiate a full chip + reset) reset) + */ +void POWER_EnterDeepSleep(uint32_t exclude_from_pd, + uint32_t sram_retention_ctrl, + uint64_t wakeup_interrupts, + uint32_t hardware_wake_ctrl); + +/** + * @brief Configures and enters in POWERDOWN low power mode + * @param exclude_from_pd: + * @param sram_retention_ctrl: + * @param wakeup_interrupts: + * @param cpu_retention_ctrl: 0 = CPU retention is disable / 1 = CPU retention is enabled, all other values are + RESERVED. + + * @return Nothing + * + * !!! IMPORTANT NOTES : + 0 - CPU0 & System CLock frequency is switched to FRO12MHz and is NOT restored back by the API. + * 1 - CPU0 Interrupt Enable registers (NVIC->ISER) are modified by this function. They are restored back in + case of CPU retention or if POWERDOWN is not taken (for instance because an interrupt is pending). + * 2 - The Non Maskable Interrupt (NMI) is disabled and its configuration before calling this function will be + restored back if POWERDOWN is not taken (for instance because an RTC or OSTIMER interrupt is pending). + * 3 - In case of CPU retention, it is the responsability of the user to make sure that SRAM instance + containing the stack used to call this function WILL BE preserved during low power (via parameter + "sram_retention_ctrl") + * 4 - The HARD FAULT handler should execute from SRAM. (The Hard fault handler should initiate a full chip + reset) reset) + */ + +void POWER_EnterPowerDown(uint32_t exclude_from_pd, + uint32_t sram_retention_ctrl, + uint64_t wakeup_interrupts, + uint32_t cpu_retention_ctrl); + +/** + * @brief Configures and enters in DEEPPOWERDOWN low power mode + * @param exclude_from_pd: + * @param sram_retention_ctrl: + * @param wakeup_interrupts: + * @param wakeup_io_ctrl: + + * @return Nothing + * + * !!! IMPORTANT NOTES : + 0 - CPU0 & System CLock frequency is switched to FRO12MHz and is NOT restored back by the API. + * 1 - CPU0 Interrupt Enable registers (NVIC->ISER) are modified by this function. They are restored back if + DEEPPOWERDOWN is not taken (for instance because an RTC or OSTIMER interrupt is pending). + * 2 - The Non Maskable Interrupt (NMI) is disabled and its configuration before calling this function will be + restored back if DEEPPOWERDOWN is not taken (for instance because an RTC or OSTIMER interrupt is pending). + * 3 - The HARD FAULT handler should execute from SRAM. (The Hard fault handler should initiate a full chip + reset) + */ +void POWER_EnterDeepPowerDown(uint32_t exclude_from_pd, + uint32_t sram_retention_ctrl, + uint64_t wakeup_interrupts, + uint32_t wakeup_io_ctrl); + +/** + * @brief Configures and enters in SLEEP low power mode + * + * @return Nothing + */ +void POWER_EnterSleep(void); + +/*! + * @brief Power Library API to choose normal regulation and set the voltage for the desired operating frequency. + * + * @param system_freq_hz - The desired frequency (in Hertz) at which the part would like to operate, + * note that the voltage and flash wait states should be set before changing frequency + * @return none + */ +void POWER_SetVoltageForFreq(uint32_t system_freq_hz); + +/** + * @brief Sets board-specific trim values for 16MHz XTAL + * @param pi32_16MfXtalIecLoadpF_x100 Load capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF becomes 120 + * @param pi32_16MfXtalPPcbParCappF_x100 PCB +ve parasitic capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF + * becomes 120 + * @param pi32_16MfXtalNPcbParCappF_x100 PCB -ve parasitic capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF + * becomes 120 + * @return none + * @note Following default Values can be used: + * pi32_32MfXtalIecLoadpF_x100 Load capacitance, pF x 100 : 600 + * pi32_32MfXtalPPcbParCappF_x100 PCB +ve parasitic capacitance, pF x 100 : 20 + * pi32_32MfXtalNPcbParCappF_x100 PCB -ve parasitic capacitance, pF x 100 : 40 + */ +extern void POWER_Xtal16mhzCapabankTrim(int32_t pi32_16MfXtalIecLoadpF_x100, + int32_t pi32_16MfXtalPPcbParCappF_x100, + int32_t pi32_16MfXtalNPcbParCappF_x100); +/** + * @brief Sets board-specific trim values for 32kHz XTAL + * @param pi32_32kfXtalIecLoadpF_x100 Load capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF becomes 120 + * @param pi32_32kfXtalPPcbParCappF_x100 PCB +ve parasitic capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF + becomes 120 + * @param pi32_32kfXtalNPcbParCappF_x100 PCB -ve parasitic capacitance, pF x 100. For example, 6pF becomes 600, 1.2pF + becomes 120 + + * @return none + * @note Following default Values can be used: + * pi32_32kfXtalIecLoadpF_x100 Load capacitance, pF x 100 : 600 + * pi32_32kfXtalPPcbParCappF_x100 PCB +ve parasitic capacitance, pF x 100 : 40 + * pi32_32kfXtalNPcbParCappF_x100 PCB -ve parasitic capacitance, pF x 100 : 40 + */ +extern void POWER_Xtal32khzCapabankTrim(int32_t pi32_32kfXtalIecLoadpF_x100, + int32_t pi32_32kfXtalPPcbParCappF_x100, + int32_t pi32_32kfXtalNPcbParCappF_x100); +/** + * @brief Enables and sets LDO for 16MHz XTAL + * @return none + */ +extern void POWER_SetXtal16mhzLdo(void); + +/** + * @brief Return some key information related to the device reset causes / wake-up sources, for all power modes. + * @param p_reset_cause : the device reset cause, according to the definition of power_device_reset_cause_t type. + * @param p_boot_mode : the device boot mode, according to the definition of power_device_boot_mode_t type. + * @param p_wakeupio_cause: the wake-up pin sources, according to the definition of register PMC->WAKEIOCAUSE[3:0]. + + * @return Nothing + * + * !!! IMPORTANT ERRATA - IMPORTANT ERRATA - IMPORTANT ERRATA !!! + * !!! valid ONLY for LPC55S69 (not for LPC55S16 and LPC55S06) !!! + * !!! when FALLING EDGE DETECTION is enabled on wake-up pins: !!! + * - 1. p_wakeupio_cause is NOT ACCURATE + * - 2. Spurious kRESET_CAUSE_DPDRESET_WAKEUPIO* event is reported when + * several wake-up sources are enabled during DEEP-POWER-DOWN + * (like enabling wake-up on RTC and Falling edge wake-up pins) + * + */ +void POWER_GetWakeUpCause(power_device_reset_cause_t *p_reset_cause, + power_device_boot_mode_t *p_boot_mode, + uint32_t *p_wakeupio_cause); +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* _FSL_POWER_H_ */ diff --git a/drivers/fsl_reset.c b/drivers/fsl_reset.c new file mode 100644 index 0000000..12e4081 --- /dev/null +++ b/drivers/fsl_reset.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016, NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_common.h" +#include "fsl_reset.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.reset" +#endif + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/******************************************************************************* + * Code + ******************************************************************************/ + +#if (defined(FSL_FEATURE_SOC_SYSCON_COUNT) && (FSL_FEATURE_SOC_SYSCON_COUNT > 0)) + +/*! + * brief Assert reset to peripheral. + * + * Asserts reset signal to specified peripheral module. + * + * param peripheral Assert reset to this peripheral. The enum argument contains encoding of reset register + * and reset bit position in the reset register. + */ +void RESET_SetPeripheralReset(reset_ip_name_t peripheral) +{ + const uint32_t regIndex = ((uint32_t)peripheral & 0xFFFF0000u) >> 16; + const uint32_t bitPos = ((uint32_t)peripheral & 0x0000FFFFu); + const uint32_t bitMask = 1UL << bitPos; + + assert(bitPos < 32u); + + /* reset register is in SYSCON */ + /* set bit */ + SYSCON->PRESETCTRLSET[regIndex] = bitMask; + /* wait until it reads 0b1 */ + while (0u == (SYSCON->PRESETCTRLX[regIndex] & bitMask)) + { + } +} + +/*! + * brief Clear reset to peripheral. + * + * Clears reset signal to specified peripheral module, allows it to operate. + * + * param peripheral Clear reset to this peripheral. The enum argument contains encoding of reset register + * and reset bit position in the reset register. + */ +void RESET_ClearPeripheralReset(reset_ip_name_t peripheral) +{ + const uint32_t regIndex = ((uint32_t)peripheral & 0xFFFF0000u) >> 16; + const uint32_t bitPos = ((uint32_t)peripheral & 0x0000FFFFu); + const uint32_t bitMask = 1UL << bitPos; + + assert(bitPos < 32u); + + /* reset register is in SYSCON */ + + /* clear bit */ + SYSCON->PRESETCTRLCLR[regIndex] = bitMask; + /* wait until it reads 0b0 */ + while (bitMask == (SYSCON->PRESETCTRLX[regIndex] & bitMask)) + { + } +} + +/*! + * brief Reset peripheral module. + * + * Reset peripheral module. + * + * param peripheral Peripheral to reset. The enum argument contains encoding of reset register + * and reset bit position in the reset register. + */ +void RESET_PeripheralReset(reset_ip_name_t peripheral) +{ + RESET_SetPeripheralReset(peripheral); + RESET_ClearPeripheralReset(peripheral); +} + +#endif /* FSL_FEATURE_SOC_SYSCON_COUNT || FSL_FEATURE_SOC_ASYNC_SYSCON_COUNT */ diff --git a/drivers/fsl_reset.h b/drivers/fsl_reset.h new file mode 100644 index 0000000..3194f68 --- /dev/null +++ b/drivers/fsl_reset.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016, NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _FSL_RESET_H_ +#define _FSL_RESET_H_ + +#include +#include +#include +#include +#include "fsl_device_registers.h" + +/*! + * @addtogroup reset + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +/*! @brief reset driver version 2.0.0. */ +#define FSL_RESET_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) +/*@}*/ + +/*! + * @brief Enumeration for peripheral reset control bits + * + * Defines the enumeration for peripheral reset control bits in PRESETCTRL/ASYNCPRESETCTRL registers + */ +typedef enum _SYSCON_RSTn +{ + kROM_RST_SHIFT_RSTn = 0 | 1U, /**< ROM reset control */ + kSRAM1_RST_SHIFT_RSTn = 0 | 3U, /**< SRAM1 reset control */ + kSRAM2_RST_SHIFT_RSTn = 0 | 4U, /**< SRAM2 reset control */ + kFLASH_RST_SHIFT_RSTn = 0 | 7U, /**< Flash controller reset control */ + kFMC_RST_SHIFT_RSTn = 0 | 8U, /**< Flash accelerator reset control */ + kMUX0_RST_SHIFT_RSTn = 0 | 11U, /**< Input mux0 reset control */ + kIOCON_RST_SHIFT_RSTn = 0 | 13U, /**< IOCON reset control */ + kGPIO0_RST_SHIFT_RSTn = 0 | 14U, /**< GPIO0 reset control */ + kGPIO1_RST_SHIFT_RSTn = 0 | 15U, /**< GPIO1 reset control */ + kPINT_RST_SHIFT_RSTn = 0 | 18U, /**< Pin interrupt (PINT) reset control */ + kGINT_RST_SHIFT_RSTn = 0 | 19U, /**< Grouped interrupt (PINT) reset control. */ + kDMA0_RST_SHIFT_RSTn = 0 | 20U, /**< DMA reset control */ + kCRC_RST_SHIFT_RSTn = 0 | 21U, /**< CRC reset control */ + kWWDT_RST_SHIFT_RSTn = 0 | 22U, /**< Watchdog timer reset control */ + kRTC_RST_SHIFT_RSTn = 0 | 23U, /**< RTC reset control */ + kMAILBOX_RST_SHIFT_RSTn = 0 | 26U, /**< Mailbox reset control */ + kADC0_RST_SHIFT_RSTn = 0 | 27U, /**< ADC0 reset control */ + + kMRT_RST_SHIFT_RSTn = 65536 | 0U, /**< Multi-rate timer (MRT) reset control */ + kOSTIMER0_RST_SHIFT_RSTn = 65536 | 1U, /**< OSTimer0 reset control */ + kSCT0_RST_SHIFT_RSTn = 65536 | 2U, /**< SCTimer/PWM 0 (SCT0) reset control */ + kMCAN_RST_SHIFT_RSTn = 65536 | 7U, /**< MCAN reset control */ + kUTICK_RST_SHIFT_RSTn = 65536 | 10U, /**< Micro-tick timer reset control */ + kFC0_RST_SHIFT_RSTn = 65536 | 11U, /**< Flexcomm Interface 0 reset control */ + kFC1_RST_SHIFT_RSTn = 65536 | 12U, /**< Flexcomm Interface 1 reset control */ + kFC2_RST_SHIFT_RSTn = 65536 | 13U, /**< Flexcomm Interface 2 reset control */ + kFC3_RST_SHIFT_RSTn = 65536 | 14U, /**< Flexcomm Interface 3 reset control */ + kFC4_RST_SHIFT_RSTn = 65536 | 15U, /**< Flexcomm Interface 4 reset control */ + kFC5_RST_SHIFT_RSTn = 65536 | 16U, /**< Flexcomm Interface 5 reset control */ + kFC6_RST_SHIFT_RSTn = 65536 | 17U, /**< Flexcomm Interface 6 reset control */ + kFC7_RST_SHIFT_RSTn = 65536 | 18U, /**< Flexcomm Interface 7 reset control */ + kCTIMER2_RST_SHIFT_RSTn = 65536 | 22U, /**< CTimer 2 reset control */ + kUSB0D_RST_SHIFT_RSTn = 65536 | 25U, /**< USB0 Device reset control */ + kCTIMER0_RST_SHIFT_RSTn = 65536 | 26U, /**< CTimer 0 reset control */ + kCTIMER1_RST_SHIFT_RSTn = 65536 | 27U, /**< CTimer 1 reset control */ + kEZHA_RST_SHIFT_RSTn = 65536 | 30U, /**< EZHA reset control */ + kEZHB_RST_SHIFT_RSTn = 65536 | 31U, /**< EZHB reset control */ + + kDMA1_RST_SHIFT_RSTn = 131072 | 1U, /**< DMA1 reset control */ + kCMP_RST_SHIFT_RSTn = 131072 | 2U, /**< CMP reset control */ + kUSB1H_RST_SHIFT_RSTn = 131072 | 4U, /**< USBHS Host reset control */ + kUSB1D_RST_SHIFT_RSTn = 131072 | 5U, /**< USBHS Device reset control */ + kUSB1RAM_RST_SHIFT_RSTn = 131072 | 6U, /**< USB RAM reset control */ + kUSB1_RST_SHIFT_RSTn = 131072 | 7U, /**< USBHS reset control */ + kFREQME_RST_SHIFT_RSTn = 131072 | 8U, /**< FREQME reset control */ + kCDOG_RST_SHIFT_RSTn = 131072 | 11U, /**< Code Watchdog reset control */ + kRNG_RST_SHIFT_RSTn = 131072 | 13U, /**< RNG reset control */ + kSYSCTL_RST_SHIFT_RSTn = 131072 | 15U, /**< SYSCTL reset control */ + kUSB0HMR_RST_SHIFT_RSTn = 131072 | 16U, /**< USB0HMR reset control */ + kUSB0HSL_RST_SHIFT_RSTn = 131072 | 17U, /**< USB0HSL reset control */ + kHASHCRYPT_RST_SHIFT_RSTn = 131072 | 18U, /**< HASHCRYPT reset control */ + kPLULUT_RST_SHIFT_RSTn = 131072 | 20U, /**< PLU LUT reset control */ + kCTIMER3_RST_SHIFT_RSTn = 131072 | 21U, /**< CTimer 3 reset control */ + kCTIMER4_RST_SHIFT_RSTn = 131072 | 22U, /**< CTimer 4 reset control */ + kPUF_RST_SHIFT_RSTn = 131072 | 23U, /**< PUF reset control */ + kCASPER_RST_SHIFT_RSTn = 131072 | 24U, /**< CASPER reset control */ + kANALOGCTL_RST_SHIFT_RSTn = 131072 | 27U, /**< ANALOG_CTL reset control */ + kHSLSPI_RST_SHIFT_RSTn = 131072 | 28U, /**< HS LSPI reset control */ + kGPIOSEC_RST_SHIFT_RSTn = 131072 | 29U, /**< GPIO Secure reset control */ + kGPIOSECINT_RST_SHIFT_RSTn = 131072 | 30U, /**< GPIO Secure int reset control */ +} SYSCON_RSTn_t; + +/** Array initializers with peripheral reset bits **/ +#define ADC_RSTS \ + { \ + kADC0_RST_SHIFT_RSTn \ + } /* Reset bits for ADC peripheral */ +#define MCAN_RSTS \ + { \ + kMCAN_RST_SHIFT_RSTn \ + } /* Reset bits for CAN peripheral */ +#define CRC_RSTS \ + { \ + kCRC_RST_SHIFT_RSTn \ + } /* Reset bits for CRC peripheral */ +#define CTIMER_RSTS \ + { \ + kCTIMER0_RST_SHIFT_RSTn, kCTIMER1_RST_SHIFT_RSTn, kCTIMER2_RST_SHIFT_RSTn, kCTIMER3_RST_SHIFT_RSTn, \ + kCTIMER4_RST_SHIFT_RSTn \ + } /* Reset bits for CTIMER peripheral */ +#define DMA_RSTS_N \ + { \ + kDMA0_RST_SHIFT_RSTn, kDMA1_RST_SHIFT_RSTn \ + } /* Reset bits for DMA peripheral */ + +#define FLEXCOMM_RSTS \ + { \ + kFC0_RST_SHIFT_RSTn, kFC1_RST_SHIFT_RSTn, kFC2_RST_SHIFT_RSTn, kFC3_RST_SHIFT_RSTn, kFC4_RST_SHIFT_RSTn, \ + kFC5_RST_SHIFT_RSTn, kFC6_RST_SHIFT_RSTn, kFC7_RST_SHIFT_RSTn, kHSLSPI_RST_SHIFT_RSTn \ + } /* Reset bits for FLEXCOMM peripheral */ +#define GINT_RSTS \ + { \ + kGINT_RST_SHIFT_RSTn, kGINT_RST_SHIFT_RSTn \ + } /* Reset bits for GINT peripheral. GINT0 & GINT1 share same slot */ +#define GPIO_RSTS_N \ + { \ + kGPIO0_RST_SHIFT_RSTn, kGPIO1_RST_SHIFT_RSTn \ + } /* Reset bits for GPIO peripheral */ +#define INPUTMUX_RSTS \ + { \ + kMUX0_RST_SHIFT_RSTn \ + } /* Reset bits for INPUTMUX peripheral */ +#define IOCON_RSTS \ + { \ + kIOCON_RST_SHIFT_RSTn \ + } /* Reset bits for IOCON peripheral */ +#define FLASH_RSTS \ + { \ + kFLASH_RST_SHIFT_RSTn, kFMC_RST_SHIFT_RSTn \ + } /* Reset bits for Flash peripheral */ +#define MRT_RSTS \ + { \ + kMRT_RST_SHIFT_RSTn \ + } /* Reset bits for MRT peripheral */ +#define PINT_RSTS \ + { \ + kPINT_RST_SHIFT_RSTn \ + } /* Reset bits for PINT peripheral */ +#define CDOG_RSTS \ + { \ + kCDOG_RST_SHIFT_RSTn \ + } /* Reset bits for CDOG peripheral */ +#define RNG_RSTS \ + { \ + kRNG_RST_SHIFT_RSTn \ + } /* Reset bits for RNG peripheral */ +#define SCT_RSTS \ + { \ + kSCT0_RST_SHIFT_RSTn \ + } /* Reset bits for SCT peripheral */ +#define USB0D_RST \ + { \ + kUSB0D_RST_SHIFT_RSTn \ + } /* Reset bits for USB0D peripheral */ +#define USB0HMR_RST \ + { \ + kUSB0HMR_RST_SHIFT_RSTn \ + } /* Reset bits for USB0HMR peripheral */ +#define USB0HSL_RST \ + { \ + kUSB0HSL_RST_SHIFT_RSTn \ + } /* Reset bits for USB0HSL peripheral */ +#define USB1H_RST \ + { \ + kUSB1H_RST_SHIFT_RSTn \ + } /* Reset bits for USB1H peripheral */ +#define USB1D_RST \ + { \ + kUSB1D_RST_SHIFT_RSTn \ + } /* Reset bits for USB1D peripheral */ +#define USB1RAM_RST \ + { \ + kUSB1RAM_RST_SHIFT_RSTn \ + } /* Reset bits for USB1RAM peripheral */ +#define UTICK_RSTS \ + { \ + kUTICK_RST_SHIFT_RSTn \ + } /* Reset bits for UTICK peripheral */ +#define WWDT_RSTS \ + { \ + kWWDT_RST_SHIFT_RSTn \ + } /* Reset bits for WWDT peripheral */ +#define PLU_RSTS_N \ + { \ + kPLULUT_RST_SHIFT_RSTn \ + } /* Reset bits for PLU peripheral */ +#define OSTIMER_RSTS \ + { \ + kOSTIMER0_RST_SHIFT_RSTn \ + } /* Reset bits for OSTIMER peripheral */ +typedef SYSCON_RSTn_t reset_ip_name_t; + +/******************************************************************************* + * API + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @brief Assert reset to peripheral. + * + * Asserts reset signal to specified peripheral module. + * + * @param peripheral Assert reset to this peripheral. The enum argument contains encoding of reset register + * and reset bit position in the reset register. + */ +void RESET_SetPeripheralReset(reset_ip_name_t peripheral); + +/*! + * @brief Clear reset to peripheral. + * + * Clears reset signal to specified peripheral module, allows it to operate. + * + * @param peripheral Clear reset to this peripheral. The enum argument contains encoding of reset register + * and reset bit position in the reset register. + */ +void RESET_ClearPeripheralReset(reset_ip_name_t peripheral); + +/*! + * @brief Reset peripheral module. + * + * Reset peripheral module. + * + * @param peripheral Peripheral to reset. The enum argument contains encoding of reset register + * and reset bit position in the reset register. + */ +void RESET_PeripheralReset(reset_ip_name_t peripheral); + +#if defined(__cplusplus) +} +#endif + +/*! @} */ + +#endif /* _FSL_RESET_H_ */ diff --git a/drivers/fsl_usart.c b/drivers/fsl_usart.c new file mode 100644 index 0000000..03cb0c9 --- /dev/null +++ b/drivers/fsl_usart.c @@ -0,0 +1,1156 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_usart.h" +#include "fsl_device_registers.h" +#include "fsl_flexcomm.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.flexcomm_usart" +#endif + +/*! + * @brief Used for conversion from `flexcomm_usart_irq_handler_t` to `flexcomm_irq_handler_t` + */ +typedef union usart_to_flexcomm +{ + flexcomm_usart_irq_handler_t usart_master_handler; + flexcomm_irq_handler_t flexcomm_handler; +} usart_to_flexcomm_t; + +enum +{ + kUSART_TxIdle, /* TX idle. */ + kUSART_TxBusy, /* TX busy. */ + kUSART_RxIdle, /* RX idle. */ + kUSART_RxBusy /* RX busy. */ +}; + +/******************************************************************************* + * Variables + ******************************************************************************/ + +/*! @brief IRQ name array */ +static const IRQn_Type s_usartIRQ[] = USART_IRQS; + +/*! @brief Array to map USART instance number to base address. */ +static const uint32_t s_usartBaseAddrs[FSL_FEATURE_SOC_USART_COUNT] = USART_BASE_ADDRS; + +/******************************************************************************* + * Code + ******************************************************************************/ + +/* Get the index corresponding to the USART */ +/*! brief Returns instance number for USART peripheral base address. */ +uint32_t USART_GetInstance(USART_Type *base) +{ + uint32_t i; + + for (i = 0; i < (uint32_t)FSL_FEATURE_SOC_USART_COUNT; i++) + { + if ((uint32_t)base == s_usartBaseAddrs[i]) + { + break; + } + } + + assert(i < (uint32_t)FSL_FEATURE_SOC_USART_COUNT); + return i; +} + +/*! + * brief Get the length of received data in RX ring buffer. + * + * param handle USART handle pointer. + * return Length of received data in RX ring buffer. + */ +size_t USART_TransferGetRxRingBufferLength(usart_handle_t *handle) +{ + size_t size; + + /* Check arguments */ + assert(NULL != handle); + uint16_t rxRingBufferHead = handle->rxRingBufferHead; + uint16_t rxRingBufferTail = handle->rxRingBufferTail; + + if (rxRingBufferTail > rxRingBufferHead) + { + size = (size_t)rxRingBufferHead + handle->rxRingBufferSize - (size_t)rxRingBufferTail; + } + else + { + size = (size_t)rxRingBufferHead - (size_t)rxRingBufferTail; + } + return size; +} + +static bool USART_TransferIsRxRingBufferFull(usart_handle_t *handle) +{ + bool full; + + /* Check arguments */ + assert(NULL != handle); + + if (USART_TransferGetRxRingBufferLength(handle) == (handle->rxRingBufferSize - 1U)) + { + full = true; + } + else + { + full = false; + } + return full; +} + +/*! + * brief Sets up the RX ring buffer. + * + * This function sets up the RX ring buffer to a specific USART handle. + * + * When the RX ring buffer is used, data received are stored into the ring buffer even when the + * user doesn't call the USART_TransferReceiveNonBlocking() API. If there is already data received + * in the ring buffer, the user can get the received data from the ring buffer directly. + * + * note When using the RX ring buffer, one byte is reserved for internal use. In other + * words, if p ringBufferSize is 32, then only 31 bytes are used for saving data. + * + * param base USART peripheral base address. + * param handle USART handle pointer. + * param ringBuffer Start address of the ring buffer for background receiving. Pass NULL to disable the ring buffer. + * param ringBufferSize size of the ring buffer. + */ +void USART_TransferStartRingBuffer(USART_Type *base, usart_handle_t *handle, uint8_t *ringBuffer, size_t ringBufferSize) +{ + /* Check arguments */ + assert(NULL != base); + assert(NULL != handle); + assert(NULL != ringBuffer); + + /* Setup the ringbuffer address */ + handle->rxRingBuffer = ringBuffer; + handle->rxRingBufferSize = ringBufferSize; + handle->rxRingBufferHead = 0U; + handle->rxRingBufferTail = 0U; + /* ring buffer is ready we can start receiving data */ + base->FIFOINTENSET = USART_FIFOINTENSET_RXLVL_MASK | USART_FIFOINTENSET_RXERR_MASK; +} + +/*! + * brief Aborts the background transfer and uninstalls the ring buffer. + * + * This function aborts the background transfer and uninstalls the ring buffer. + * + * param base USART peripheral base address. + * param handle USART handle pointer. + */ +void USART_TransferStopRingBuffer(USART_Type *base, usart_handle_t *handle) +{ + /* Check arguments */ + assert(NULL != base); + assert(NULL != handle); + + if (handle->rxState == (uint8_t)kUSART_RxIdle) + { + base->FIFOINTENCLR = USART_FIFOINTENCLR_RXLVL_MASK | USART_FIFOINTENCLR_RXERR_MASK; + } + handle->rxRingBuffer = NULL; + handle->rxRingBufferSize = 0U; + handle->rxRingBufferHead = 0U; + handle->rxRingBufferTail = 0U; +} + +/*! + * brief Initializes a USART instance with user configuration structure and peripheral clock. + * + * This function configures the USART module with the user-defined settings. The user can configure the configuration + * structure and also get the default configuration by using the USART_GetDefaultConfig() function. + * Example below shows how to use this API to configure USART. + * code + * usart_config_t usartConfig; + * usartConfig.baudRate_Bps = 115200U; + * usartConfig.parityMode = kUSART_ParityDisabled; + * usartConfig.stopBitCount = kUSART_OneStopBit; + * USART_Init(USART1, &usartConfig, 20000000U); + * endcode + * + * param base USART peripheral base address. + * param config Pointer to user-defined configuration structure. + * param srcClock_Hz USART clock source frequency in HZ. + * retval kStatus_USART_BaudrateNotSupport Baudrate is not support in current clock source. + * retval kStatus_InvalidArgument USART base address is not valid + * retval kStatus_Success Status USART initialize succeed + */ +status_t USART_Init(USART_Type *base, const usart_config_t *config, uint32_t srcClock_Hz) +{ + int result; + + /* check arguments */ + assert(!((NULL == base) || (NULL == config) || (0U == srcClock_Hz))); + if ((NULL == base) || (NULL == config) || (0U == srcClock_Hz)) + { + return kStatus_InvalidArgument; + } + + /* initialize flexcomm to USART mode */ + result = FLEXCOMM_Init(base, FLEXCOMM_PERIPH_USART); + if (kStatus_Success != result) + { + return result; + } + + if (config->enableTx) + { + /* empty and enable txFIFO */ + base->FIFOCFG |= USART_FIFOCFG_EMPTYTX_MASK | USART_FIFOCFG_ENABLETX_MASK; + /* setup trigger level */ + base->FIFOTRIG &= ~(USART_FIFOTRIG_TXLVL_MASK); + base->FIFOTRIG |= USART_FIFOTRIG_TXLVL(config->txWatermark); + /* enable trigger interrupt */ + base->FIFOTRIG |= USART_FIFOTRIG_TXLVLENA_MASK; + } + + /* empty and enable rxFIFO */ + if (config->enableRx) + { + base->FIFOCFG |= USART_FIFOCFG_EMPTYRX_MASK | USART_FIFOCFG_ENABLERX_MASK; + /* setup trigger level */ + base->FIFOTRIG &= ~(USART_FIFOTRIG_RXLVL_MASK); + base->FIFOTRIG |= USART_FIFOTRIG_RXLVL(config->rxWatermark); + /* enable trigger interrupt */ + base->FIFOTRIG |= USART_FIFOTRIG_RXLVLENA_MASK; + } + /* setup configuration and enable USART */ + base->CFG = USART_CFG_PARITYSEL(config->parityMode) | USART_CFG_STOPLEN(config->stopBitCount) | + USART_CFG_DATALEN(config->bitCountPerChar) | USART_CFG_LOOP(config->loopback) | + USART_CFG_SYNCEN((uint32_t)config->syncMode >> 1) | USART_CFG_SYNCMST((uint8_t)config->syncMode) | + USART_CFG_CLKPOL(config->clockPolarity) | USART_CFG_MODE32K(config->enableMode32k) | + USART_CFG_CTSEN(config->enableHardwareFlowControl) | USART_CFG_ENABLE_MASK; + + /* Setup baudrate */ + if (config->enableMode32k) + { + if ((9600U % config->baudRate_Bps) == 0U) + { + base->BRG = 9600U / config->baudRate_Bps; + } + else + { + return kStatus_USART_BaudrateNotSupport; + } + } + else + { + result = USART_SetBaudRate(base, config->baudRate_Bps, srcClock_Hz); + if (kStatus_Success != result) + { + return result; + } + } + /* Setting continuous Clock configuration. used for synchronous mode. */ + USART_EnableContinuousSCLK(base, config->enableContinuousSCLK); + + return kStatus_Success; +} + +/*! + * brief Deinitializes a USART instance. + * + * This function waits for TX complete, disables TX and RX, and disables the USART clock. + * + * param base USART peripheral base address. + */ +void USART_Deinit(USART_Type *base) +{ + /* Check arguments */ + assert(NULL != base); + while (0U == (base->STAT & USART_STAT_TXIDLE_MASK)) + { + } + /* Disable interrupts, disable dma requests, disable peripheral */ + base->FIFOINTENCLR = USART_FIFOINTENCLR_TXERR_MASK | USART_FIFOINTENCLR_RXERR_MASK | USART_FIFOINTENCLR_TXLVL_MASK | + USART_FIFOINTENCLR_RXLVL_MASK; + base->FIFOCFG &= ~(USART_FIFOCFG_DMATX_MASK | USART_FIFOCFG_DMARX_MASK); + base->CFG &= ~(USART_CFG_ENABLE_MASK); +} + +/*! + * brief Gets the default configuration structure. + * + * This function initializes the USART configuration structure to a default value. The default + * values are: + * usartConfig->baudRate_Bps = 115200U; + * usartConfig->parityMode = kUSART_ParityDisabled; + * usartConfig->stopBitCount = kUSART_OneStopBit; + * usartConfig->bitCountPerChar = kUSART_8BitsPerChar; + * usartConfig->loopback = false; + * usartConfig->enableTx = false; + * usartConfig->enableRx = false; + * + * param config Pointer to configuration structure. + */ +void USART_GetDefaultConfig(usart_config_t *config) +{ + /* Check arguments */ + assert(NULL != config); + + /* Initializes the configure structure to zero. */ + (void)memset(config, 0, sizeof(*config)); + + /* Set always all members ! */ + config->baudRate_Bps = 115200U; + config->parityMode = kUSART_ParityDisabled; + config->stopBitCount = kUSART_OneStopBit; + config->bitCountPerChar = kUSART_8BitsPerChar; + config->loopback = false; + config->enableRx = false; + config->enableTx = false; + config->enableMode32k = false; + config->txWatermark = kUSART_TxFifo0; + config->rxWatermark = kUSART_RxFifo1; + config->syncMode = kUSART_SyncModeDisabled; + config->enableContinuousSCLK = false; + config->clockPolarity = kUSART_RxSampleOnFallingEdge; + config->enableHardwareFlowControl = false; +} + +/*! + * brief Sets the USART instance baud rate. + * + * This function configures the USART module baud rate. This function is used to update + * the USART module baud rate after the USART module is initialized by the USART_Init. + * code + * USART_SetBaudRate(USART1, 115200U, 20000000U); + * endcode + * + * param base USART peripheral base address. + * param baudrate_Bps USART baudrate to be set. + * param srcClock_Hz USART clock source frequency in HZ. + * retval kStatus_USART_BaudrateNotSupport Baudrate is not support in current clock source. + * retval kStatus_Success Set baudrate succeed. + * retval kStatus_InvalidArgument One or more arguments are invalid. + */ +status_t USART_SetBaudRate(USART_Type *base, uint32_t baudrate_Bps, uint32_t srcClock_Hz) +{ + uint32_t best_diff = (uint32_t)-1, best_osrval = 0xf, best_brgval = (uint32_t)-1; + uint32_t osrval, brgval, diff, baudrate; + + /* check arguments */ + assert(!((NULL == base) || (0U == baudrate_Bps) || (0U == srcClock_Hz))); + if ((NULL == base) || (0U == baudrate_Bps) || (0U == srcClock_Hz)) + { + return kStatus_InvalidArgument; + } + + /* If synchronous master mode is enabled, only configure the BRG value. */ + if ((base->CFG & USART_CFG_SYNCEN_MASK) != 0U) + { + if ((base->CFG & USART_CFG_SYNCMST_MASK) != 0U) + { + brgval = srcClock_Hz / baudrate_Bps; + base->BRG = brgval - 1U; + } + } + else + { + /* + * Smaller values of OSR can make the sampling position within a data bit less accurate and may + * potentially cause more noise errors or incorrect data. + */ + for (osrval = best_osrval; osrval >= 8U; osrval--) + { + brgval = (((srcClock_Hz * 10U) / ((osrval + 1U) * baudrate_Bps)) - 5U) / 10U; + if (brgval > 0xFFFFU) + { + continue; + } + baudrate = srcClock_Hz / ((osrval + 1U) * (brgval + 1U)); + diff = (baudrate_Bps < baudrate) ? (baudrate - baudrate_Bps) : (baudrate_Bps - baudrate); + if (diff < best_diff) + { + best_diff = diff; + best_osrval = osrval; + best_brgval = brgval; + } + } + + /* Check to see if actual baud rate is within 3% of desired baud rate + * based on the best calculated OSR and BRG value */ + baudrate = srcClock_Hz / ((best_osrval + 1U) * (best_brgval + 1U)); + diff = (baudrate_Bps < baudrate) ? (baudrate - baudrate_Bps) : (baudrate_Bps - baudrate); + if (diff > ((baudrate_Bps / 100U) * 3U)) + { + return kStatus_USART_BaudrateNotSupport; + } + + /* value over range */ + if (best_brgval > 0xFFFFU) + { + return kStatus_USART_BaudrateNotSupport; + } + + base->OSR = best_osrval; + base->BRG = best_brgval; + } + + return kStatus_Success; +} + +/*! + * brief Enable 32 kHz mode which USART uses clock from the RTC oscillator as the clock source. + * + * Please note that in order to use a 32 kHz clock to operate USART properly, the RTC oscillator + * and its 32 kHz output must be manully enabled by user, by calling RTC_Init and setting + * SYSCON_RTCOSCCTRL_EN bit to 1. + * And in 32kHz clocking mode the USART can only work at 9600 baudrate or at the baudrate that + * 9600 can evenly divide, eg: 4800, 3200. + * + * param base USART peripheral base address. + * param baudRate_Bps USART baudrate to be set.. + * param enableMode32k true is 32k mode, false is normal mode. + * param srcClock_Hz USART clock source frequency in HZ. + * retval kStatus_USART_BaudrateNotSupport Baudrate is not support in current clock source. + * retval kStatus_Success Set baudrate succeed. + * retval kStatus_InvalidArgument One or more arguments are invalid. + */ +status_t USART_Enable32kMode(USART_Type *base, uint32_t baudRate_Bps, bool enableMode32k, uint32_t srcClock_Hz) +{ + status_t result = kStatus_Success; + base->CFG &= ~(USART_CFG_ENABLE_MASK); + if (enableMode32k) + { + base->CFG |= USART_CFG_MODE32K_MASK; + if ((9600U % baudRate_Bps) == 0U) + { + base->BRG = 9600U / baudRate_Bps - 1U; + } + else + { + return kStatus_USART_BaudrateNotSupport; + } + } + else + { + base->CFG &= ~(USART_CFG_MODE32K_MASK); + result = USART_SetBaudRate(base, baudRate_Bps, srcClock_Hz); + if (kStatus_Success != result) + { + return result; + } + } + base->CFG |= USART_CFG_ENABLE_MASK; + return result; +} + +/*! + * brief Enable 9-bit data mode for USART. + * + * This function set the 9-bit mode for USART module. The 9th bit is not used for parity thus can be modified by user. + * + * param base USART peripheral base address. + * param enable true to enable, false to disable. + */ +void USART_Enable9bitMode(USART_Type *base, bool enable) +{ + assert(base != NULL); + + uint32_t temp = 0U; + + if (enable) + { + /* Set USART 9-bit mode, disable parity. */ + temp = base->CFG & ~((uint32_t)USART_CFG_DATALEN_MASK | (uint32_t)USART_CFG_PARITYSEL_MASK); + temp |= (uint32_t)USART_CFG_DATALEN(0x2U); + base->CFG = temp; + } + else + { + /* Set USART to 8-bit mode. */ + base->CFG &= ~((uint32_t)USART_CFG_DATALEN_MASK); + base->CFG |= (uint32_t)USART_CFG_DATALEN(0x1U); + } +} + +/*! + * brief Transmit an address frame in 9-bit data mode. + * + * param base USART peripheral base address. + * param address USART slave address. + */ +void USART_SendAddress(USART_Type *base, uint8_t address) +{ + assert(base != NULL); + base->FIFOWR = ((uint32_t)address | 0x100UL); +} + +/*! + * brief Writes to the TX register using a blocking method. + * + * This function polls the TX register, waits for the TX register to be empty or for the TX FIFO + * to have room and writes data to the TX buffer. + * + * param base USART peripheral base address. + * param data Start address of the data to write. + * param length Size of the data to write. + * retval kStatus_USART_Timeout Transmission timed out and was aborted. + * retval kStatus_InvalidArgument Invalid argument. + * retval kStatus_Success Successfully wrote all data. + */ +status_t USART_WriteBlocking(USART_Type *base, const uint8_t *data, size_t length) +{ + /* Check arguments */ + assert(!((NULL == base) || (NULL == data))); +#if UART_RETRY_TIMES + uint32_t waitTimes; +#endif + if ((NULL == base) || (NULL == data)) + { + return kStatus_InvalidArgument; + } + /* Check whether txFIFO is enabled */ + if (0U == (base->FIFOCFG & USART_FIFOCFG_ENABLETX_MASK)) + { + return kStatus_InvalidArgument; + } + for (; length > 0U; length--) + { + /* Loop until txFIFO get some space for new data */ +#if UART_RETRY_TIMES + waitTimes = UART_RETRY_TIMES; + while ((0U == (base->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK)) && (--waitTimes != 0U)) +#else + while (0U == (base->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK)) +#endif + { + } +#if UART_RETRY_TIMES + if (0U == waitTimes) + { + return kStatus_USART_Timeout; + } +#endif + base->FIFOWR = *data; + data++; + } + /* Wait to finish transfer */ +#if UART_RETRY_TIMES + waitTimes = UART_RETRY_TIMES; + while ((0U == (base->STAT & USART_STAT_TXIDLE_MASK)) && (--waitTimes != 0U)) +#else + while (0U == (base->STAT & USART_STAT_TXIDLE_MASK)) +#endif + { + } +#if UART_RETRY_TIMES + if (0U == waitTimes) + { + return kStatus_USART_Timeout; + } +#endif + return kStatus_Success; +} + +/*! + * brief Read RX data register using a blocking method. + * + * This function polls the RX register, waits for the RX register to be full or for RX FIFO to + * have data and read data from the TX register. + * + * param base USART peripheral base address. + * param data Start address of the buffer to store the received data. + * param length Size of the buffer. + * retval kStatus_USART_FramingError Receiver overrun happened while receiving data. + * retval kStatus_USART_ParityError Noise error happened while receiving data. + * retval kStatus_USART_NoiseError Framing error happened while receiving data. + * retval kStatus_USART_RxError Overflow or underflow rxFIFO happened. + * retval kStatus_USART_Timeout Transmission timed out and was aborted. + * retval kStatus_Success Successfully received all data. + */ +status_t USART_ReadBlocking(USART_Type *base, uint8_t *data, size_t length) +{ + uint32_t statusFlag; + status_t status = kStatus_Success; +#if UART_RETRY_TIMES + uint32_t waitTimes; +#endif + + /* check arguments */ + assert(!((NULL == base) || (NULL == data))); + if ((NULL == base) || (NULL == data)) + { + return kStatus_InvalidArgument; + } + + /* Check whether rxFIFO is enabled */ + if ((base->FIFOCFG & USART_FIFOCFG_ENABLERX_MASK) == 0U) + { + return kStatus_Fail; + } + for (; length > 0U; length--) + { + /* loop until rxFIFO have some data to read */ +#if UART_RETRY_TIMES + waitTimes = UART_RETRY_TIMES; + while (((base->FIFOSTAT & USART_FIFOSTAT_RXNOTEMPTY_MASK) == 0U) && (--waitTimes != 0U)) +#else + while ((base->FIFOSTAT & USART_FIFOSTAT_RXNOTEMPTY_MASK) == 0U) +#endif + { + } +#if UART_RETRY_TIMES + if (waitTimes == 0U) + { + status = kStatus_USART_Timeout; + break; + } +#endif + /* check rxFIFO statusFlag */ + if ((base->FIFOSTAT & USART_FIFOSTAT_RXERR_MASK) != 0U) + { + base->FIFOCFG |= USART_FIFOCFG_EMPTYRX_MASK; + base->FIFOSTAT |= USART_FIFOSTAT_RXERR_MASK; + status = kStatus_USART_RxError; + break; + } + /* check receive statusFlag */ + statusFlag = base->STAT; + /* Clear all status flags */ + base->STAT |= statusFlag; + if ((statusFlag & USART_STAT_PARITYERRINT_MASK) != 0U) + { + status = kStatus_USART_ParityError; + } + if ((statusFlag & USART_STAT_FRAMERRINT_MASK) != 0U) + { + status = kStatus_USART_FramingError; + } + if ((statusFlag & USART_STAT_RXNOISEINT_MASK) != 0U) + { + status = kStatus_USART_NoiseError; + } + + if (kStatus_Success == status) + { + *data = (uint8_t)base->FIFORD; + data++; + } + else + { + break; + } + } + return status; +} + +/*! + * brief Initializes the USART handle. + * + * This function initializes the USART handle which can be used for other USART + * transactional APIs. Usually, for a specified USART instance, + * call this API once to get the initialized handle. + * + * param base USART peripheral base address. + * param handle USART handle pointer. + * param callback The callback function. + * param userData The parameter of the callback function. + */ +status_t USART_TransferCreateHandle(USART_Type *base, + usart_handle_t *handle, + usart_transfer_callback_t callback, + void *userData) +{ + /* Check 'base' */ + assert(!((NULL == base) || (NULL == handle))); + + uint32_t instance = 0; + usart_to_flexcomm_t handler; + handler.usart_master_handler = USART_TransferHandleIRQ; + + if ((NULL == base) || (NULL == handle)) + { + return kStatus_InvalidArgument; + } + + instance = USART_GetInstance(base); + + (void)memset(handle, 0, sizeof(*handle)); + /* Set the TX/RX state. */ + handle->rxState = (uint8_t)kUSART_RxIdle; + handle->txState = (uint8_t)kUSART_TxIdle; + /* Set the callback and user data. */ + handle->callback = callback; + handle->userData = userData; + handle->rxWatermark = (uint8_t)USART_FIFOTRIG_RXLVL_GET(base); + handle->txWatermark = (uint8_t)USART_FIFOTRIG_TXLVL_GET(base); + + FLEXCOMM_SetIRQHandler(base, handler.flexcomm_handler, handle); + + /* Enable interrupt in NVIC. */ + (void)EnableIRQ(s_usartIRQ[instance]); + + return kStatus_Success; +} + +/*! + * brief Transmits a buffer of data using the interrupt method. + * + * This function sends data using an interrupt method. This is a non-blocking function, which + * returns directly without waiting for all data to be written to the TX register. When + * all data is written to the TX register in the IRQ handler, the USART driver calls the callback + * function and passes the ref kStatus_USART_TxIdle as status parameter. + * + * param base USART peripheral base address. + * param handle USART handle pointer. + * param xfer USART transfer structure. See #usart_transfer_t. + * retval kStatus_Success Successfully start the data transmission. + * retval kStatus_USART_TxBusy Previous transmission still not finished, data not all written to TX register yet. + * retval kStatus_InvalidArgument Invalid argument. + */ +status_t USART_TransferSendNonBlocking(USART_Type *base, usart_handle_t *handle, usart_transfer_t *xfer) +{ + /* Check arguments */ + assert(!((NULL == base) || (NULL == handle) || (NULL == xfer))); + if ((NULL == base) || (NULL == handle) || (NULL == xfer)) + { + return kStatus_InvalidArgument; + } + /* Check xfer members */ + assert(!((0U == xfer->dataSize) || (NULL == xfer->txData))); + if ((0U == xfer->dataSize) || (NULL == xfer->txData)) + { + return kStatus_InvalidArgument; + } + + /* Return error if current TX busy. */ + if ((uint8_t)kUSART_TxBusy == handle->txState) + { + return kStatus_USART_TxBusy; + } + else + { + /* Disable IRQ when configuring transfer handle, in case interrupt occurs during the process and messes up the + * handle value. */ + uint32_t interruptMask = USART_GetEnabledInterrupts(base); + USART_DisableInterrupts(base, interruptMask); + handle->txData = xfer->txData; + handle->txDataSize = xfer->dataSize; + handle->txDataSizeAll = xfer->dataSize; + handle->txState = (uint8_t)kUSART_TxBusy; + /* Enable transmiter interrupt and the previously disabled interrupt. */ + USART_EnableInterrupts(base, interruptMask | (uint32_t)kUSART_TxLevelInterruptEnable); + } + return kStatus_Success; +} + +/*! + * brief Aborts the interrupt-driven data transmit. + * + * This function aborts the interrupt driven data sending. The user can get the remainBtyes to find out + * how many bytes are still not sent out. + * + * param base USART peripheral base address. + * param handle USART handle pointer. + */ +void USART_TransferAbortSend(USART_Type *base, usart_handle_t *handle) +{ + assert(NULL != handle); + + /* Disable interrupts */ + USART_DisableInterrupts(base, (uint32_t)kUSART_TxLevelInterruptEnable); + /* Empty txFIFO */ + base->FIFOCFG |= USART_FIFOCFG_EMPTYTX_MASK; + + handle->txDataSize = 0U; + handle->txState = (uint8_t)kUSART_TxIdle; +} + +/*! + * brief Get the number of bytes that have been sent out to bus. + * + * This function gets the number of bytes that have been sent out to bus by interrupt method. + * + * param base USART peripheral base address. + * param handle USART handle pointer. + * param count Send bytes count. + * retval kStatus_NoTransferInProgress No send in progress. + * retval kStatus_InvalidArgument Parameter is invalid. + * retval kStatus_Success Get successfully through the parameter \p count; + */ +status_t USART_TransferGetSendCount(USART_Type *base, usart_handle_t *handle, uint32_t *count) +{ + assert(NULL != handle); + assert(NULL != count); + + if ((uint8_t)kUSART_TxIdle == handle->txState) + { + return kStatus_NoTransferInProgress; + } + + *count = handle->txDataSizeAll - handle->txDataSize - + ((base->FIFOSTAT & USART_FIFOSTAT_TXLVL_MASK) >> USART_FIFOSTAT_TXLVL_SHIFT); + + return kStatus_Success; +} + +/*! + * brief Receives a buffer of data using an interrupt method. + * + * This function receives data using an interrupt method. This is a non-blocking function, which + * returns without waiting for all data to be received. + * If the RX ring buffer is used and not empty, the data in the ring buffer is copied and + * the parameter p receivedBytes shows how many bytes are copied from the ring buffer. + * After copying, if the data in the ring buffer is not enough to read, the receive + * request is saved by the USART driver. When the new data arrives, the receive request + * is serviced first. When all data is received, the USART driver notifies the upper layer + * through a callback function and passes the status parameter ref kStatus_USART_RxIdle. + * For example, the upper layer needs 10 bytes but there are only 5 bytes in the ring buffer. + * The 5 bytes are copied to the xfer->data and this function returns with the + * parameter p receivedBytes set to 5. For the left 5 bytes, newly arrived data is + * saved from the xfer->data[5]. When 5 bytes are received, the USART driver notifies the upper layer. + * If the RX ring buffer is not enabled, this function enables the RX and RX interrupt + * to receive data to the xfer->data. When all data is received, the upper layer is notified. + * + * param base USART peripheral base address. + * param handle USART handle pointer. + * param xfer USART transfer structure, see #usart_transfer_t. + * param receivedBytes Bytes received from the ring buffer directly. + * retval kStatus_Success Successfully queue the transfer into transmit queue. + * retval kStatus_USART_RxBusy Previous receive request is not finished. + * retval kStatus_InvalidArgument Invalid argument. + */ +status_t USART_TransferReceiveNonBlocking(USART_Type *base, + usart_handle_t *handle, + usart_transfer_t *xfer, + size_t *receivedBytes) +{ + uint32_t i; + /* How many bytes to copy from ring buffer to user memory. */ + size_t bytesToCopy = 0U; + /* How many bytes to receive. */ + size_t bytesToReceive; + /* How many bytes currently have received. */ + size_t bytesCurrentReceived; + uint32_t interruptMask = 0U; + + /* Check arguments */ + assert(!((NULL == base) || (NULL == handle) || (NULL == xfer))); + if ((NULL == base) || (NULL == handle) || (NULL == xfer)) + { + return kStatus_InvalidArgument; + } + /* Check xfer members */ + assert(!((0U == xfer->dataSize) || (NULL == xfer->rxData))); + if ((0U == xfer->dataSize) || (NULL == xfer->rxData)) + { + return kStatus_InvalidArgument; + } + + /* Enable address detect when address match is enabled. */ + if ((base->CFG & (uint32_t)USART_CFG_AUTOADDR_MASK) != 0U) + { + base->CTL |= (uint32_t)USART_CTL_ADDRDET_MASK; + } + + /* How to get data: + 1. If RX ring buffer is not enabled, then save xfer->data and xfer->dataSize + to uart handle, enable interrupt to store received data to xfer->data. When + all data received, trigger callback. + 2. If RX ring buffer is enabled and not empty, get data from ring buffer first. + If there are enough data in ring buffer, copy them to xfer->data and return. + If there are not enough data in ring buffer, copy all of them to xfer->data, + save the xfer->data remained empty space to uart handle, receive data + to this empty space and trigger callback when finished. */ + if ((uint8_t)kUSART_RxBusy == handle->rxState) + { + return kStatus_USART_RxBusy; + } + else + { + bytesToReceive = xfer->dataSize; + bytesCurrentReceived = 0U; + /* If RX ring buffer is used. */ + if (handle->rxRingBuffer != NULL) + { + /* Disable IRQ, protect ring buffer. */ + interruptMask = USART_GetEnabledInterrupts(base); + USART_DisableInterrupts(base, interruptMask); + + /* How many bytes in RX ring buffer currently. */ + bytesToCopy = USART_TransferGetRxRingBufferLength(handle); + if (bytesToCopy != 0U) + { + bytesToCopy = MIN(bytesToReceive, bytesToCopy); + bytesToReceive -= bytesToCopy; + /* Copy data from ring buffer to user memory. */ + for (i = 0U; i < bytesToCopy; i++) + { + xfer->rxData[bytesCurrentReceived++] = handle->rxRingBuffer[handle->rxRingBufferTail]; + /* Wrap to 0. Not use modulo (%) because it might be large and slow. */ + if ((size_t)handle->rxRingBufferTail + 1U == handle->rxRingBufferSize) + { + handle->rxRingBufferTail = 0U; + } + else + { + handle->rxRingBufferTail++; + } + } + } + /* If ring buffer does not have enough data, still need to read more data. */ + if (bytesToReceive != 0U) + { + /* No data in ring buffer, save the request to UART handle. */ + handle->rxData = xfer->rxData + bytesCurrentReceived; + handle->rxDataSize = bytesToReceive; + handle->rxDataSizeAll = xfer->dataSize; + handle->rxState = (uint8_t)kUSART_RxBusy; + } + /* Re-enable IRQ. */ + USART_EnableInterrupts(base, interruptMask); + /* Call user callback since all data are received. */ + if (0U == bytesToReceive) + { + if (handle->callback != NULL) + { + handle->callback(base, handle, kStatus_USART_RxIdle, handle->userData); + } + } + } + /* Ring buffer not used. */ + else + { + /* Disable IRQ when configuring transfer handle, in case interrupt occurs during the process and messes up + * the handle value. */ + interruptMask = USART_GetEnabledInterrupts(base); + USART_DisableInterrupts(base, interruptMask); + handle->rxData = xfer->rxData + bytesCurrentReceived; + handle->rxDataSize = bytesToReceive; + handle->rxDataSizeAll = bytesToReceive; + handle->rxState = (uint8_t)kUSART_RxBusy; + + /* Enable RX interrupt. */ + base->FIFOINTENSET = USART_FIFOINTENSET_RXLVL_MASK; + /* Re-enable IRQ. */ + USART_EnableInterrupts(base, interruptMask); + } + /* Return the how many bytes have read. */ + if (receivedBytes != NULL) + { + *receivedBytes = bytesCurrentReceived; + } + } + return kStatus_Success; +} + +/*! + * brief Aborts the interrupt-driven data receiving. + * + * This function aborts the interrupt-driven data receiving. The user can get the remainBytes to find out + * how many bytes not received yet. + * + * param base USART peripheral base address. + * param handle USART handle pointer. + */ +void USART_TransferAbortReceive(USART_Type *base, usart_handle_t *handle) +{ + assert(NULL != handle); + + /* Only abort the receive to handle->rxData, the RX ring buffer is still working. */ + if (NULL == handle->rxRingBuffer) + { + /* Disable interrupts */ + USART_DisableInterrupts(base, (uint32_t)kUSART_RxLevelInterruptEnable); + /* Empty rxFIFO */ + base->FIFOCFG |= USART_FIFOCFG_EMPTYRX_MASK; + } + + handle->rxDataSize = 0U; + handle->rxState = (uint8_t)kUSART_RxIdle; +} + +/*! + * brief Get the number of bytes that have been received. + * + * This function gets the number of bytes that have been received. + * + * param base USART peripheral base address. + * param handle USART handle pointer. + * param count Receive bytes count. + * retval kStatus_NoTransferInProgress No receive in progress. + * retval kStatus_InvalidArgument Parameter is invalid. + * retval kStatus_Success Get successfully through the parameter \p count; + */ +status_t USART_TransferGetReceiveCount(USART_Type *base, usart_handle_t *handle, uint32_t *count) +{ + assert(NULL != handle); + assert(NULL != count); + + if ((uint8_t)kUSART_RxIdle == handle->rxState) + { + return kStatus_NoTransferInProgress; + } + + *count = handle->rxDataSizeAll - handle->rxDataSize; + + return kStatus_Success; +} + +/*! + * brief USART IRQ handle function. + * + * This function handles the USART transmit and receive IRQ request. + * + * param base USART peripheral base address. + * param handle USART handle pointer. + */ +void USART_TransferHandleIRQ(USART_Type *base, usart_handle_t *handle) +{ + /* Check arguments */ + assert((NULL != base) && (NULL != handle)); + + bool receiveEnabled = ((handle->rxDataSize != 0U) || (handle->rxRingBuffer != NULL)); + bool sendEnabled = (handle->txDataSize != 0U); + uint8_t rxdata; + size_t tmpsize; + + /* If RX overrun. */ + if ((base->FIFOSTAT & USART_FIFOSTAT_RXERR_MASK) != 0U) + { + /* Clear rx error state. */ + base->FIFOSTAT |= USART_FIFOSTAT_RXERR_MASK; + /* clear rxFIFO */ + base->FIFOCFG |= USART_FIFOCFG_EMPTYRX_MASK; + /* Trigger callback. */ + if (handle->callback != NULL) + { + handle->callback(base, handle, kStatus_USART_RxError, handle->userData); + } + } + while ((receiveEnabled && ((base->FIFOSTAT & USART_FIFOSTAT_RXNOTEMPTY_MASK) != 0U)) || + (sendEnabled && ((base->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK) != 0U))) + { + /* Receive data */ + if (receiveEnabled && ((base->FIFOSTAT & USART_FIFOSTAT_RXNOTEMPTY_MASK) != 0U)) + { + /* Clear address detect when RXFIFO has data. */ + base->CTL &= ~(uint32_t)USART_CTL_ADDRDET_MASK; + /* Receive to app bufffer if app buffer is present */ + if (handle->rxDataSize != 0U) + { + rxdata = (uint8_t)base->FIFORD; + *handle->rxData = rxdata; + handle->rxDataSize--; + handle->rxData++; + receiveEnabled = ((handle->rxDataSize != 0U) || (handle->rxRingBuffer != NULL)); + if (0U == handle->rxDataSize) + { + if (NULL == handle->rxRingBuffer) + { + base->FIFOINTENCLR = USART_FIFOINTENCLR_RXLVL_MASK | USART_FIFOINTENSET_RXERR_MASK; + } + handle->rxState = (uint8_t)kUSART_RxIdle; + if (handle->callback != NULL) + { + handle->callback(base, handle, kStatus_USART_RxIdle, handle->userData); + } + } + } + /* Otherwise receive to ring buffer if ring buffer is present */ + else + { + if (handle->rxRingBuffer != NULL) + { + /* If RX ring buffer is full, trigger callback to notify over run. */ + if (USART_TransferIsRxRingBufferFull(handle)) + { + if (handle->callback != NULL) + { + handle->callback(base, handle, kStatus_USART_RxRingBufferOverrun, handle->userData); + } + } + /* If ring buffer is still full after callback function, the oldest data is overridden. */ + if (USART_TransferIsRxRingBufferFull(handle)) + { + /* Increase handle->rxRingBufferTail to make room for new data. */ + if ((size_t)handle->rxRingBufferTail + 1U == handle->rxRingBufferSize) + { + handle->rxRingBufferTail = 0U; + } + else + { + handle->rxRingBufferTail++; + } + } + /* Read data. */ + rxdata = (uint8_t)base->FIFORD; + handle->rxRingBuffer[handle->rxRingBufferHead] = rxdata; + /* Increase handle->rxRingBufferHead. */ + if ((size_t)handle->rxRingBufferHead + 1U == handle->rxRingBufferSize) + { + handle->rxRingBufferHead = 0U; + } + else + { + handle->rxRingBufferHead++; + } + } + } + } + /* Send data */ + if (sendEnabled && ((base->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK) != 0U)) + { + base->FIFOWR = *handle->txData; + handle->txDataSize--; + handle->txData++; + sendEnabled = handle->txDataSize != 0U; + if (!sendEnabled) + { + base->FIFOINTENCLR = USART_FIFOINTENCLR_TXLVL_MASK; + + base->INTENSET = USART_INTENSET_TXIDLEEN_MASK; + } + } + } + + /* Tx idle and the interrupt is enabled. */ + if ((0U != (base->INTENSET & USART_INTENSET_TXIDLEEN_MASK)) && (0U != (base->INTSTAT & USART_INTSTAT_TXIDLE_MASK))) + { + /* Set txState to idle only when all data has been sent out to bus. */ + handle->txState = (uint8_t)kUSART_TxIdle; + /* Disable tx idle interrupt */ + base->INTENCLR = USART_INTENCLR_TXIDLECLR_MASK; + + /* Trigger callback. */ + if (handle->callback != NULL) + { + handle->callback(base, handle, kStatus_USART_TxIdle, handle->userData); + } + } + + /* ring buffer is not used */ + if (NULL == handle->rxRingBuffer) + { + tmpsize = handle->rxDataSize; + + /* restore if rx transfer ends and rxLevel is different from default value */ + if ((tmpsize == 0U) && (USART_FIFOTRIG_RXLVL_GET(base) != handle->rxWatermark)) + { + base->FIFOTRIG = + (base->FIFOTRIG & (~USART_FIFOTRIG_RXLVL_MASK)) | USART_FIFOTRIG_RXLVL(handle->rxWatermark); + } + /* decrease level if rx transfer is bellow */ + if ((tmpsize != 0U) && (tmpsize < (USART_FIFOTRIG_RXLVL_GET(base) + 1U))) + { + base->FIFOTRIG = (base->FIFOTRIG & (~USART_FIFOTRIG_RXLVL_MASK)) | (USART_FIFOTRIG_RXLVL(tmpsize - 1U)); + } + } +} diff --git a/drivers/fsl_usart.h b/drivers/fsl_usart.h new file mode 100644 index 0000000..53af19b --- /dev/null +++ b/drivers/fsl_usart.h @@ -0,0 +1,862 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_USART_H_ +#define _FSL_USART_H_ + +#include "fsl_common.h" + +/*! + * @addtogroup usart_driver + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +/*! @brief USART driver version. */ +#define FSL_USART_DRIVER_VERSION (MAKE_VERSION(2, 6, 0)) +/*@}*/ + +#define USART_FIFOTRIG_TXLVL_GET(base) (((base)->FIFOTRIG & USART_FIFOTRIG_TXLVL_MASK) >> USART_FIFOTRIG_TXLVL_SHIFT) +#define USART_FIFOTRIG_RXLVL_GET(base) (((base)->FIFOTRIG & USART_FIFOTRIG_RXLVL_MASK) >> USART_FIFOTRIG_RXLVL_SHIFT) + +/*! @brief Retry times for waiting flag. */ +#ifndef UART_RETRY_TIMES +#define UART_RETRY_TIMES 0U /* Defining to zero means to keep waiting for the flag until it is assert/deassert. */ +#endif + +/*! @brief Error codes for the USART driver. */ +enum +{ + kStatus_USART_TxBusy = MAKE_STATUS(kStatusGroup_LPC_USART, 0), /*!< Transmitter is busy. */ + kStatus_USART_RxBusy = MAKE_STATUS(kStatusGroup_LPC_USART, 1), /*!< Receiver is busy. */ + kStatus_USART_TxIdle = MAKE_STATUS(kStatusGroup_LPC_USART, 2), /*!< USART transmitter is idle. */ + kStatus_USART_RxIdle = MAKE_STATUS(kStatusGroup_LPC_USART, 3), /*!< USART receiver is idle. */ + kStatus_USART_TxError = MAKE_STATUS(kStatusGroup_LPC_USART, 7), /*!< Error happens on txFIFO. */ + kStatus_USART_RxError = MAKE_STATUS(kStatusGroup_LPC_USART, 9), /*!< Error happens on rxFIFO. */ + kStatus_USART_RxRingBufferOverrun = MAKE_STATUS(kStatusGroup_LPC_USART, 8), /*!< Error happens on rx ring buffer */ + kStatus_USART_NoiseError = MAKE_STATUS(kStatusGroup_LPC_USART, 10), /*!< USART noise error. */ + kStatus_USART_FramingError = MAKE_STATUS(kStatusGroup_LPC_USART, 11), /*!< USART framing error. */ + kStatus_USART_ParityError = MAKE_STATUS(kStatusGroup_LPC_USART, 12), /*!< USART parity error. */ + kStatus_USART_BaudrateNotSupport = + MAKE_STATUS(kStatusGroup_LPC_USART, 13), /*!< Baudrate is not support in current clock source */ + kStatus_USART_Timeout = MAKE_STATUS(kStatusGroup_LPC_USART, 14), /*!< USART time out. */ +}; + +/*! @brief USART synchronous mode. */ +typedef enum _usart_sync_mode +{ + kUSART_SyncModeDisabled = 0x0U, /*!< Asynchronous mode. */ + kUSART_SyncModeSlave = 0x2U, /*!< Synchronous slave mode. */ + kUSART_SyncModeMaster = 0x3U, /*!< Synchronous master mode. */ +} usart_sync_mode_t; + +/*! @brief USART parity mode. */ +typedef enum _usart_parity_mode +{ + kUSART_ParityDisabled = 0x0U, /*!< Parity disabled */ + kUSART_ParityEven = 0x2U, /*!< Parity enabled, type even, bit setting: PE|PT = 10 */ + kUSART_ParityOdd = 0x3U, /*!< Parity enabled, type odd, bit setting: PE|PT = 11 */ +} usart_parity_mode_t; + +/*! @brief USART stop bit count. */ +typedef enum _usart_stop_bit_count +{ + kUSART_OneStopBit = 0U, /*!< One stop bit */ + kUSART_TwoStopBit = 1U, /*!< Two stop bits */ +} usart_stop_bit_count_t; + +/*! @brief USART data size. */ +typedef enum _usart_data_len +{ + kUSART_7BitsPerChar = 0U, /*!< Seven bit mode */ + kUSART_8BitsPerChar = 1U, /*!< Eight bit mode */ +} usart_data_len_t; + +/*! @brief USART clock polarity configuration, used in sync mode.*/ +typedef enum _usart_clock_polarity +{ + kUSART_RxSampleOnFallingEdge = 0x0U, /*!< Un_RXD is sampled on the falling edge of SCLK. */ + kUSART_RxSampleOnRisingEdge = 0x1U, /*!< Un_RXD is sampled on the rising edge of SCLK. */ +} usart_clock_polarity_t; + +/*! @brief txFIFO watermark values */ +typedef enum _usart_txfifo_watermark +{ + kUSART_TxFifo0 = 0, /*!< USART tx watermark is empty */ + kUSART_TxFifo1 = 1, /*!< USART tx watermark at 1 item */ + kUSART_TxFifo2 = 2, /*!< USART tx watermark at 2 items */ + kUSART_TxFifo3 = 3, /*!< USART tx watermark at 3 items */ + kUSART_TxFifo4 = 4, /*!< USART tx watermark at 4 items */ + kUSART_TxFifo5 = 5, /*!< USART tx watermark at 5 items */ + kUSART_TxFifo6 = 6, /*!< USART tx watermark at 6 items */ + kUSART_TxFifo7 = 7, /*!< USART tx watermark at 7 items */ +} usart_txfifo_watermark_t; + +/*! @brief rxFIFO watermark values */ +typedef enum _usart_rxfifo_watermark +{ + kUSART_RxFifo1 = 0, /*!< USART rx watermark at 1 item */ + kUSART_RxFifo2 = 1, /*!< USART rx watermark at 2 items */ + kUSART_RxFifo3 = 2, /*!< USART rx watermark at 3 items */ + kUSART_RxFifo4 = 3, /*!< USART rx watermark at 4 items */ + kUSART_RxFifo5 = 4, /*!< USART rx watermark at 5 items */ + kUSART_RxFifo6 = 5, /*!< USART rx watermark at 6 items */ + kUSART_RxFifo7 = 6, /*!< USART rx watermark at 7 items */ + kUSART_RxFifo8 = 7, /*!< USART rx watermark at 8 items */ +} usart_rxfifo_watermark_t; + +/*! + * @brief USART interrupt configuration structure, default settings all disabled. + */ +enum _usart_interrupt_enable +{ + kUSART_TxErrorInterruptEnable = (USART_FIFOINTENSET_TXERR_MASK), + kUSART_RxErrorInterruptEnable = (USART_FIFOINTENSET_RXERR_MASK), + kUSART_TxLevelInterruptEnable = (USART_FIFOINTENSET_TXLVL_MASK), + kUSART_RxLevelInterruptEnable = (USART_FIFOINTENSET_RXLVL_MASK), +}; + +/*! + * @brief USART status flags. + * + * This provides constants for the USART status flags for use in the USART functions. + */ +enum _usart_flags +{ + kUSART_TxError = (USART_FIFOSTAT_TXERR_MASK), /*!< TEERR bit, sets if TX buffer is error */ + kUSART_RxError = (USART_FIFOSTAT_RXERR_MASK), /*!< RXERR bit, sets if RX buffer is error */ + kUSART_TxFifoEmptyFlag = (USART_FIFOSTAT_TXEMPTY_MASK), /*!< TXEMPTY bit, sets if TX buffer is empty */ + kUSART_TxFifoNotFullFlag = (USART_FIFOSTAT_TXNOTFULL_MASK), /*!< TXNOTFULL bit, sets if TX buffer is not full */ + kUSART_RxFifoNotEmptyFlag = (USART_FIFOSTAT_RXNOTEMPTY_MASK), /*!< RXNOEMPTY bit, sets if RX buffer is not empty */ + kUSART_RxFifoFullFlag = (USART_FIFOSTAT_RXFULL_MASK), /*!< RXFULL bit, sets if RX buffer is full */ +}; + +/*! @brief USART configuration structure. */ +typedef struct _usart_config +{ + uint32_t baudRate_Bps; /*!< USART baud rate */ + usart_parity_mode_t parityMode; /*!< Parity mode, disabled (default), even, odd */ + usart_stop_bit_count_t stopBitCount; /*!< Number of stop bits, 1 stop bit (default) or 2 stop bits */ + usart_data_len_t bitCountPerChar; /*!< Data length - 7 bit, 8 bit */ + bool loopback; /*!< Enable peripheral loopback */ + bool enableRx; /*!< Enable RX */ + bool enableTx; /*!< Enable TX */ + bool enableContinuousSCLK; /*!< USART continuous Clock generation enable in synchronous master mode. */ + bool enableMode32k; /*!< USART uses 32 kHz clock from the RTC oscillator as the clock source. */ + bool enableHardwareFlowControl; /*!< Enable hardware control RTS/CTS */ + usart_txfifo_watermark_t txWatermark; /*!< txFIFO watermark */ + usart_rxfifo_watermark_t rxWatermark; /*!< rxFIFO watermark */ + usart_sync_mode_t syncMode; /*!< Transfer mode select - asynchronous, synchronous master, synchronous slave. */ + usart_clock_polarity_t clockPolarity; /*!< Selects the clock polarity and sampling edge in synchronous mode. */ +} usart_config_t; + +/*! @brief USART transfer structure. */ +typedef struct _usart_transfer +{ + /* + * Use separate TX and RX data pointer, because TX data is const data. + * The member data is kept for backward compatibility. + */ + union + { + uint8_t *data; /*!< The buffer of data to be transfer.*/ + uint8_t *rxData; /*!< The buffer to receive data. */ + const uint8_t *txData; /*!< The buffer of data to be sent. */ + }; + size_t dataSize; /*!< The byte count to be transfer. */ +} usart_transfer_t; + +/* Forward declaration of the handle typedef. */ +typedef struct _usart_handle usart_handle_t; + +/*! @brief USART transfer callback function. */ +typedef void (*usart_transfer_callback_t)(USART_Type *base, usart_handle_t *handle, status_t status, void *userData); + +/*! @brief USART handle structure. */ +struct _usart_handle +{ + const uint8_t *volatile txData; /*!< Address of remaining data to send. */ + volatile size_t txDataSize; /*!< Size of the remaining data to send. */ + size_t txDataSizeAll; /*!< Size of the data to send out. */ + uint8_t *volatile rxData; /*!< Address of remaining data to receive. */ + volatile size_t rxDataSize; /*!< Size of the remaining data to receive. */ + size_t rxDataSizeAll; /*!< Size of the data to receive. */ + + uint8_t *rxRingBuffer; /*!< Start address of the receiver ring buffer. */ + size_t rxRingBufferSize; /*!< Size of the ring buffer. */ + volatile uint16_t rxRingBufferHead; /*!< Index for the driver to store received data into ring buffer. */ + volatile uint16_t rxRingBufferTail; /*!< Index for the user to get data from the ring buffer. */ + + usart_transfer_callback_t callback; /*!< Callback function. */ + void *userData; /*!< USART callback function parameter.*/ + + volatile uint8_t txState; /*!< TX transfer state. */ + volatile uint8_t rxState; /*!< RX transfer state */ + + uint8_t txWatermark; /*!< txFIFO watermark */ + uint8_t rxWatermark; /*!< rxFIFO watermark */ +}; + +/*! @brief Typedef for usart interrupt handler. */ +typedef void (*flexcomm_usart_irq_handler_t)(USART_Type *base, usart_handle_t *handle); + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif /* _cplusplus */ + +/*! @brief Returns instance number for USART peripheral base address. */ +uint32_t USART_GetInstance(USART_Type *base); + +/*! + * @name Initialization and deinitialization + * @{ + */ + +/*! + * @brief Initializes a USART instance with user configuration structure and peripheral clock. + * + * This function configures the USART module with the user-defined settings. The user can configure the configuration + * structure and also get the default configuration by using the USART_GetDefaultConfig() function. + * Example below shows how to use this API to configure USART. + * @code + * usart_config_t usartConfig; + * usartConfig.baudRate_Bps = 115200U; + * usartConfig.parityMode = kUSART_ParityDisabled; + * usartConfig.stopBitCount = kUSART_OneStopBit; + * USART_Init(USART1, &usartConfig, 20000000U); + * @endcode + * + * @param base USART peripheral base address. + * @param config Pointer to user-defined configuration structure. + * @param srcClock_Hz USART clock source frequency in HZ. + * @retval kStatus_USART_BaudrateNotSupport Baudrate is not support in current clock source. + * @retval kStatus_InvalidArgument USART base address is not valid + * @retval kStatus_Success Status USART initialize succeed + */ +status_t USART_Init(USART_Type *base, const usart_config_t *config, uint32_t srcClock_Hz); + +/*! + * @brief Deinitializes a USART instance. + * + * This function waits for TX complete, disables TX and RX, and disables the USART clock. + * + * @param base USART peripheral base address. + */ +void USART_Deinit(USART_Type *base); + +/*! + * @brief Gets the default configuration structure. + * + * This function initializes the USART configuration structure to a default value. The default + * values are: + * usartConfig->baudRate_Bps = 115200U; + * usartConfig->parityMode = kUSART_ParityDisabled; + * usartConfig->stopBitCount = kUSART_OneStopBit; + * usartConfig->bitCountPerChar = kUSART_8BitsPerChar; + * usartConfig->loopback = false; + * usartConfig->enableTx = false; + * usartConfig->enableRx = false; + * + * @param config Pointer to configuration structure. + */ +void USART_GetDefaultConfig(usart_config_t *config); + +/*! + * @brief Sets the USART instance baud rate. + * + * This function configures the USART module baud rate. This function is used to update + * the USART module baud rate after the USART module is initialized by the USART_Init. + * @code + * USART_SetBaudRate(USART1, 115200U, 20000000U); + * @endcode + * + * @param base USART peripheral base address. + * @param baudrate_Bps USART baudrate to be set. + * @param srcClock_Hz USART clock source frequency in HZ. + * @retval kStatus_USART_BaudrateNotSupport Baudrate is not support in current clock source. + * @retval kStatus_Success Set baudrate succeed. + * @retval kStatus_InvalidArgument One or more arguments are invalid. + */ +status_t USART_SetBaudRate(USART_Type *base, uint32_t baudrate_Bps, uint32_t srcClock_Hz); + +/*! + * @brief Enable 32 kHz mode which USART uses clock from the RTC oscillator as the clock source + * + * Please note that in order to use a 32 kHz clock to operate USART properly, the RTC oscillator + * and its 32 kHz output must be manully enabled by user, by calling RTC_Init and setting + * SYSCON_RTCOSCCTRL_EN bit to 1. + * And in 32kHz clocking mode the USART can only work at 9600 baudrate or at the baudrate that + * 9600 can evenly divide, eg: 4800, 3200. + * + * @param base USART peripheral base address. + * @param baudRate_Bps USART baudrate to be set.. + * @param enableMode32k true is 32k mode, false is normal mode. + * @param srcClock_Hz USART clock source frequency in HZ. + * @retval kStatus_USART_BaudrateNotSupport Baudrate is not support in current clock source. + * @retval kStatus_Success Set baudrate succeed. + * @retval kStatus_InvalidArgument One or more arguments are invalid. + */ +status_t USART_Enable32kMode(USART_Type *base, uint32_t baudRate_Bps, bool enableMode32k, uint32_t srcClock_Hz); + +/*! + * @brief Enable 9-bit data mode for USART. + * + * This function set the 9-bit mode for USART module. The 9th bit is not used for parity thus can be modified by user. + * + * @param base USART peripheral base address. + * @param enable true to enable, false to disable. + */ +void USART_Enable9bitMode(USART_Type *base, bool enable); + +/*! + * @brief Set the USART slave address. + * + * This function configures the address for USART module that works as slave in 9-bit data mode. When the address + * detection is enabled, the frame it receices with MSB being 1 is considered as an address frame, otherwise it is + * considered as data frame. Once the address frame matches slave's own addresses, this slave is addressed. This + * address frame and its following data frames are stored in the receive buffer, otherwise the frames will be discarded. + * To un-address a slave, just send an address frame with unmatched address. + * + * @note Any USART instance joined in the multi-slave system can work as slave. The position of the address mark is the + * same as the parity bit when parity is enabled for 8 bit and 9 bit data formats. + * + * @param base USART peripheral base address. + * @param address USART slave address. + */ +static inline void USART_SetMatchAddress(USART_Type *base, uint8_t address) +{ + /* Configure match address. */ + base->ADDR = (uint32_t)address; +} + +/*! + * @brief Enable the USART match address feature. + * + * @param base USART peripheral base address. + * @param match true to enable match address, false to disable. + */ +static inline void USART_EnableMatchAddress(USART_Type *base, bool match) +{ + /* Configure match address enable bit. */ + if (match) + { + base->CFG |= (uint32_t)USART_CFG_AUTOADDR_MASK; + base->CTL |= (uint32_t)USART_CTL_ADDRDET_MASK; + } + else + { + base->CFG &= ~(uint32_t)USART_CFG_AUTOADDR_MASK; + base->CTL &= ~(uint32_t)USART_CTL_ADDRDET_MASK; + } +} + +/* @} */ + +/*! + * @name Status + * @{ + */ + +/*! + * @brief Get USART status flags. + * + * This function get all USART status flags, the flags are returned as the logical + * OR value of the enumerators @ref _usart_flags. To check a specific status, + * compare the return value with enumerators in @ref _usart_flags. + * For example, to check whether the TX is empty: + * @code + * if (kUSART_TxFifoNotFullFlag & USART_GetStatusFlags(USART1)) + * { + * ... + * } + * @endcode + * + * @param base USART peripheral base address. + * @return USART status flags which are ORed by the enumerators in the _usart_flags. + */ +static inline uint32_t USART_GetStatusFlags(USART_Type *base) +{ + return base->FIFOSTAT; +} + +/*! + * @brief Clear USART status flags. + * + * This function clear supported USART status flags + * Flags that can be cleared or set are: + * kUSART_TxError + * kUSART_RxError + * For example: + * @code + * USART_ClearStatusFlags(USART1, kUSART_TxError | kUSART_RxError) + * @endcode + * + * @param base USART peripheral base address. + * @param mask status flags to be cleared. + */ +static inline void USART_ClearStatusFlags(USART_Type *base, uint32_t mask) +{ + /* Only TXERR, RXERR fields support write. Remaining fields should be set to zero */ + base->FIFOSTAT = mask & (USART_FIFOSTAT_TXERR_MASK | USART_FIFOSTAT_RXERR_MASK); +} + +/* @} */ + +/*! + * @name Interrupts + * @{ + */ + +/*! + * @brief Enables USART interrupts according to the provided mask. + * + * This function enables the USART interrupts according to the provided mask. The mask + * is a logical OR of enumeration members. See @ref _usart_interrupt_enable. + * For example, to enable TX empty interrupt and RX full interrupt: + * @code + * USART_EnableInterrupts(USART1, kUSART_TxLevelInterruptEnable | kUSART_RxLevelInterruptEnable); + * @endcode + * + * @param base USART peripheral base address. + * @param mask The interrupts to enable. Logical OR of @ref _usart_interrupt_enable. + */ +static inline void USART_EnableInterrupts(USART_Type *base, uint32_t mask) +{ + base->FIFOINTENSET = mask & 0xFUL; +} + +/*! + * @brief Disables USART interrupts according to a provided mask. + * + * This function disables the USART interrupts according to a provided mask. The mask + * is a logical OR of enumeration members. See @ref _usart_interrupt_enable. + * This example shows how to disable the TX empty interrupt and RX full interrupt: + * @code + * USART_DisableInterrupts(USART1, kUSART_TxLevelInterruptEnable | kUSART_RxLevelInterruptEnable); + * @endcode + * + * @param base USART peripheral base address. + * @param mask The interrupts to disable. Logical OR of @ref _usart_interrupt_enable. + */ +static inline void USART_DisableInterrupts(USART_Type *base, uint32_t mask) +{ + base->FIFOINTENCLR = mask & 0xFUL; +} + +/*! + * @brief Returns enabled USART interrupts. + * + * This function returns the enabled USART interrupts. + * + * @param base USART peripheral base address. + */ +static inline uint32_t USART_GetEnabledInterrupts(USART_Type *base) +{ + return base->FIFOINTENSET; +} + +/*! + * @brief Enable DMA for Tx + */ +static inline void USART_EnableTxDMA(USART_Type *base, bool enable) +{ + if (enable) + { + base->FIFOCFG |= USART_FIFOCFG_DMATX_MASK; + } + else + { + base->FIFOCFG &= ~(USART_FIFOCFG_DMATX_MASK); + } +} + +/*! + * @brief Enable DMA for Rx + */ +static inline void USART_EnableRxDMA(USART_Type *base, bool enable) +{ + if (enable) + { + base->FIFOCFG |= USART_FIFOCFG_DMARX_MASK; + } + else + { + base->FIFOCFG &= ~(USART_FIFOCFG_DMARX_MASK); + } +} + +/*! + * @brief Enable CTS. + * This function will determine whether CTS is used for flow control. + * + * @param base USART peripheral base address. + * @param enable Enable CTS or not, true for enable and false for disable. + */ +static inline void USART_EnableCTS(USART_Type *base, bool enable) +{ + if (enable) + { + base->CFG |= USART_CFG_CTSEN_MASK; + } + else + { + base->CFG &= ~USART_CFG_CTSEN_MASK; + } +} + +/*! + * @brief Continuous Clock generation. + * By default, SCLK is only output while data is being transmitted in synchronous mode. + * Enable this funciton, SCLK will run continuously in synchronous mode, allowing + * characters to be received on Un_RxD independently from transmission on Un_TXD). + * + * @param base USART peripheral base address. + * @param enable Enable Continuous Clock generation mode or not, true for enable and false for disable. + */ +static inline void USART_EnableContinuousSCLK(USART_Type *base, bool enable) +{ + if (enable) + { + base->CTL |= USART_CTL_CC_MASK; + } + else + { + base->CTL &= ~USART_CTL_CC_MASK; + } +} + +/*! + * @brief Enable Continuous Clock generation bit auto clear. + * While enable this cuntion, the Continuous Clock bit is automatically cleared when a complete + * character has been received. This bit is cleared at the same time. + * + * @param base USART peripheral base address. + * @param enable Enable auto clear or not, true for enable and false for disable. + */ +static inline void USART_EnableAutoClearSCLK(USART_Type *base, bool enable) +{ + if (enable) + { + base->CTL |= USART_CTL_CLRCCONRX_MASK; + } + else + { + base->CTL &= ~USART_CTL_CLRCCONRX_MASK; + } +} + +/*! + * @brief Sets the rx FIFO watermark. + * + * @param base USART peripheral base address. + * @param water Rx FIFO watermark. + */ +static inline void USART_SetRxFifoWatermark(USART_Type *base, uint8_t water) +{ + assert(water <= (USART_FIFOTRIG_RXLVL_MASK >> USART_FIFOTRIG_RXLVL_SHIFT)); + base->FIFOTRIG = (base->FIFOTRIG & ~USART_FIFOTRIG_RXLVL_MASK) | USART_FIFOTRIG_RXLVL(water); +} + +/*! + * @brief Sets the tx FIFO watermark. + * + * @param base USART peripheral base address. + * @param water Tx FIFO watermark. + */ +static inline void USART_SetTxFifoWatermark(USART_Type *base, uint8_t water) +{ + assert(water <= (USART_FIFOTRIG_TXLVL_MASK >> USART_FIFOTRIG_TXLVL_SHIFT)); + base->FIFOTRIG = (base->FIFOTRIG & ~USART_FIFOTRIG_TXLVL_MASK) | USART_FIFOTRIG_TXLVL(water); +} +/* @} */ + +/*! + * @name Bus Operations + * @{ + */ + +/*! + * @brief Writes to the FIFOWR register. + * + * This function writes data to the txFIFO directly. The upper layer must ensure + * that txFIFO has space for data to write before calling this function. + * + * @param base USART peripheral base address. + * @param data The byte to write. + */ +static inline void USART_WriteByte(USART_Type *base, uint8_t data) +{ + base->FIFOWR = data; +} + +/*! + * @brief Reads the FIFORD register directly. + * + * This function reads data from the rxFIFO directly. The upper layer must + * ensure that the rxFIFO is not empty before calling this function. + * + * @param base USART peripheral base address. + * @return The byte read from USART data register. + */ +static inline uint8_t USART_ReadByte(USART_Type *base) +{ + return (uint8_t)base->FIFORD; +} + +/*! + * @brief Gets the rx FIFO data count. + * + * @param base USART peripheral base address. + * @return rx FIFO data count. + */ +static inline uint8_t USART_GetRxFifoCount(USART_Type *base) +{ + return (uint8_t)((base->FIFOSTAT & USART_FIFOSTAT_RXLVL_MASK) >> USART_FIFOSTAT_RXLVL_SHIFT); +} + +/*! + * @brief Gets the tx FIFO data count. + * + * @param base USART peripheral base address. + * @return tx FIFO data count. + */ +static inline uint8_t USART_GetTxFifoCount(USART_Type *base) +{ + return (uint8_t)((base->FIFOSTAT & USART_FIFOSTAT_TXLVL_MASK) >> USART_FIFOSTAT_TXLVL_SHIFT); +} + +/*! + * @brief Transmit an address frame in 9-bit data mode. + * + * @param base USART peripheral base address. + * @param address USART slave address. + */ +void USART_SendAddress(USART_Type *base, uint8_t address); + +/*! + * @brief Writes to the TX register using a blocking method. + * + * This function polls the TX register, waits for the TX register to be empty or for the TX FIFO + * to have room and writes data to the TX buffer. + * + * @param base USART peripheral base address. + * @param data Start address of the data to write. + * @param length Size of the data to write. + * @retval kStatus_USART_Timeout Transmission timed out and was aborted. + * @retval kStatus_InvalidArgument Invalid argument. + * @retval kStatus_Success Successfully wrote all data. + */ +status_t USART_WriteBlocking(USART_Type *base, const uint8_t *data, size_t length); + +/*! + * @brief Read RX data register using a blocking method. + * + * This function polls the RX register, waits for the RX register to be full or for RX FIFO to + * have data and read data from the TX register. + * + * @param base USART peripheral base address. + * @param data Start address of the buffer to store the received data. + * @param length Size of the buffer. + * @retval kStatus_USART_FramingError Receiver overrun happened while receiving data. + * @retval kStatus_USART_ParityError Noise error happened while receiving data. + * @retval kStatus_USART_NoiseError Framing error happened while receiving data. + * @retval kStatus_USART_RxError Overflow or underflow rxFIFO happened. + * @retval kStatus_USART_Timeout Transmission timed out and was aborted. + * @retval kStatus_Success Successfully received all data. + */ +status_t USART_ReadBlocking(USART_Type *base, uint8_t *data, size_t length); + +/* @} */ + +/*! + * @name Transactional + * @{ + */ + +/*! + * @brief Initializes the USART handle. + * + * This function initializes the USART handle which can be used for other USART + * transactional APIs. Usually, for a specified USART instance, + * call this API once to get the initialized handle. + * + * @param base USART peripheral base address. + * @param handle USART handle pointer. + * @param callback The callback function. + * @param userData The parameter of the callback function. + */ +status_t USART_TransferCreateHandle(USART_Type *base, + usart_handle_t *handle, + usart_transfer_callback_t callback, + void *userData); + +/*! + * @brief Transmits a buffer of data using the interrupt method. + * + * This function sends data using an interrupt method. This is a non-blocking function, which + * returns directly without waiting for all data to be written to the TX register. When + * all data is written to the TX register in the IRQ handler, the USART driver calls the callback + * function and passes the @ref kStatus_USART_TxIdle as status parameter. + * + * @param base USART peripheral base address. + * @param handle USART handle pointer. + * @param xfer USART transfer structure. See #usart_transfer_t. + * @retval kStatus_Success Successfully start the data transmission. + * @retval kStatus_USART_TxBusy Previous transmission still not finished, data not all written to TX register yet. + * @retval kStatus_InvalidArgument Invalid argument. + */ +status_t USART_TransferSendNonBlocking(USART_Type *base, usart_handle_t *handle, usart_transfer_t *xfer); + +/*! + * @brief Sets up the RX ring buffer. + * + * This function sets up the RX ring buffer to a specific USART handle. + * + * When the RX ring buffer is used, data received are stored into the ring buffer even when the + * user doesn't call the USART_TransferReceiveNonBlocking() API. If there is already data received + * in the ring buffer, the user can get the received data from the ring buffer directly. + * + * @note When using the RX ring buffer, one byte is reserved for internal use. In other + * words, if @p ringBufferSize is 32, then only 31 bytes are used for saving data. + * + * @param base USART peripheral base address. + * @param handle USART handle pointer. + * @param ringBuffer Start address of the ring buffer for background receiving. Pass NULL to disable the ring buffer. + * @param ringBufferSize size of the ring buffer. + */ +void USART_TransferStartRingBuffer(USART_Type *base, + usart_handle_t *handle, + uint8_t *ringBuffer, + size_t ringBufferSize); + +/*! + * @brief Aborts the background transfer and uninstalls the ring buffer. + * + * This function aborts the background transfer and uninstalls the ring buffer. + * + * @param base USART peripheral base address. + * @param handle USART handle pointer. + */ +void USART_TransferStopRingBuffer(USART_Type *base, usart_handle_t *handle); + +/*! + * @brief Get the length of received data in RX ring buffer. + * + * @param handle USART handle pointer. + * @return Length of received data in RX ring buffer. + */ +size_t USART_TransferGetRxRingBufferLength(usart_handle_t *handle); + +/*! + * @brief Aborts the interrupt-driven data transmit. + * + * This function aborts the interrupt driven data sending. The user can get the remainBtyes to find out + * how many bytes are still not sent out. + * + * @param base USART peripheral base address. + * @param handle USART handle pointer. + */ +void USART_TransferAbortSend(USART_Type *base, usart_handle_t *handle); + +/*! + * @brief Get the number of bytes that have been sent out to bus. + * + * This function gets the number of bytes that have been sent out to bus by interrupt method. + * + * @param base USART peripheral base address. + * @param handle USART handle pointer. + * @param count Send bytes count. + * @retval kStatus_NoTransferInProgress No send in progress. + * @retval kStatus_InvalidArgument Parameter is invalid. + * @retval kStatus_Success Get successfully through the parameter \p count; + */ +status_t USART_TransferGetSendCount(USART_Type *base, usart_handle_t *handle, uint32_t *count); + +/*! + * @brief Receives a buffer of data using an interrupt method. + * + * This function receives data using an interrupt method. This is a non-blocking function, which + * returns without waiting for all data to be received. + * If the RX ring buffer is used and not empty, the data in the ring buffer is copied and + * the parameter @p receivedBytes shows how many bytes are copied from the ring buffer. + * After copying, if the data in the ring buffer is not enough to read, the receive + * request is saved by the USART driver. When the new data arrives, the receive request + * is serviced first. When all data is received, the USART driver notifies the upper layer + * through a callback function and passes the status parameter @ref kStatus_USART_RxIdle. + * For example, the upper layer needs 10 bytes but there are only 5 bytes in the ring buffer. + * The 5 bytes are copied to the xfer->data and this function returns with the + * parameter @p receivedBytes set to 5. For the left 5 bytes, newly arrived data is + * saved from the xfer->data[5]. When 5 bytes are received, the USART driver notifies the upper layer. + * If the RX ring buffer is not enabled, this function enables the RX and RX interrupt + * to receive data to the xfer->data. When all data is received, the upper layer is notified. + * + * @param base USART peripheral base address. + * @param handle USART handle pointer. + * @param xfer USART transfer structure, see #usart_transfer_t. + * @param receivedBytes Bytes received from the ring buffer directly. + * @retval kStatus_Success Successfully queue the transfer into transmit queue. + * @retval kStatus_USART_RxBusy Previous receive request is not finished. + * @retval kStatus_InvalidArgument Invalid argument. + */ +status_t USART_TransferReceiveNonBlocking(USART_Type *base, + usart_handle_t *handle, + usart_transfer_t *xfer, + size_t *receivedBytes); + +/*! + * @brief Aborts the interrupt-driven data receiving. + * + * This function aborts the interrupt-driven data receiving. The user can get the remainBytes to find out + * how many bytes not received yet. + * + * @param base USART peripheral base address. + * @param handle USART handle pointer. + */ +void USART_TransferAbortReceive(USART_Type *base, usart_handle_t *handle); + +/*! + * @brief Get the number of bytes that have been received. + * + * This function gets the number of bytes that have been received. + * + * @param base USART peripheral base address. + * @param handle USART handle pointer. + * @param count Receive bytes count. + * @retval kStatus_NoTransferInProgress No receive in progress. + * @retval kStatus_InvalidArgument Parameter is invalid. + * @retval kStatus_Success Get successfully through the parameter \p count; + */ +status_t USART_TransferGetReceiveCount(USART_Type *base, usart_handle_t *handle, uint32_t *count); + +/*! + * @brief USART IRQ handle function. + * + * This function handles the USART transmit and receive IRQ request. + * + * @param base USART peripheral base address. + * @param handle USART handle pointer. + */ +void USART_TransferHandleIRQ(USART_Type *base, usart_handle_t *handle); + +/* @} */ + +#if defined(__cplusplus) +} +#endif + +/*! @}*/ + +#endif /* _FSL_USART_H_ */