/** * @file mxc_sys.c * @brief System level setup help */ /******************************************************************************* * Copyright (C) 2015 Maxim Integrated Products, Inc., All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of Maxim Integrated * Products, Inc. shall not be used except as stated in the Maxim Integrated * Products, Inc. Branding Policy. * * The mere transfer of this software does not imply any licenses * of trade secrets, proprietary technology, copyrights, patents, * trademarks, maskwork rights, or any other form of intellectual * property whatsoever. Maxim Integrated Products, Inc. retains all * ownership rights. * * $Date: 2020-01-17 08:38:51 -0600 (Fri, 17 Jan 2020) $ * $Revision: 50772 $ * ******************************************************************************/ #include #include "mxc_config.h" #include "mxc_assert.h" #include "mxc_sys.h" #include "gpio.h" #include "mxc_pins.h" #include "gcr_regs.h" #include "tmr_regs.h" #include "pwrseq_regs.h" #include "spi17y_regs.h" #include "spimss_regs.h" #include "mxc_delay.h" #include "rtc.h" /** * @ingroup MXC_sys * @{ */ /***** Definitions *****/ #define SYS_CLOCK_TIMEOUT MXC_DELAY_MSEC(1) #define SYS_RTC_CLK 32768UL /***** Functions ******/ static int SYS_Clock_Timeout(uint32_t ready) { // Start timeout, wait for ready mxc_delay_start(SYS_CLOCK_TIMEOUT); do { if (MXC_GCR->clkcn & ready) { mxc_delay_stop(); return E_NO_ERROR; } } while (mxc_delay_check() == E_BUSY); return E_TIME_OUT; } /* ************************************************************************ */ int SYS_Clock_Select(sys_system_clock_t clock, mxc_tmr_regs_t* tmr) { uint32_t current_clock,ovr, div; // Save the current system clock current_clock = MXC_GCR->clkcn & MXC_F_GCR_CLKCN_CLKSEL; // Set FWS higher than what the minimum for the fastest clock is MXC_GCR->memckcn = (MXC_GCR->memckcn & ~(MXC_F_GCR_MEMCKCN_FWS)) | (0x5UL << MXC_F_GCR_MEMCKCN_FWS_POS); switch(clock) { case SYS_CLOCK_NANORING: // Set NANORING clock as System Clock MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, MXC_S_GCR_CLKCN_CLKSEL_NANORING); break; case SYS_CLOCK_HFXIN: // Enable 32k Oscillator MXC_GCR->clkcn |=MXC_F_GCR_CLKCN_X32K_EN; // Check if 32k clock is ready if (SYS_Clock_Timeout(MXC_F_GCR_CLKCN_X32K_RDY) != E_NO_ERROR) { return E_TIME_OUT; } MXC_RTC->ctrl |= MXC_F_RTC_CTRL_WE; // Allow writing to registers // Set 32k clock as System Clock MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, MXC_S_GCR_CLKCN_CLKSEL_HFXIN); break; case SYS_CLOCK_HFXIN_DIGITAL: // Enable 32k Oscillator MXC_GCR->clkcn |=MXC_F_GCR_CLKCN_X32K_EN; // Check if 32k clock is ready if (SYS_Clock_Timeout(MXC_F_GCR_CLKCN_X32K_RDY) != E_NO_ERROR) { return E_TIME_OUT; } MXC_RTC->ctrl |= MXC_F_RTC_CTRL_WE; // Allow writing to registers MXC_RTC->oscctrl |= MXC_F_RTC_OSCCTRL_BYPASS; // To allow square wave driven on 32KIN // Set 32k clock as System Clock MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, MXC_S_GCR_CLKCN_CLKSEL_HFXIN); break; case SYS_CLOCK_HIRC: // Enable 96MHz Clock MXC_GCR->clkcn |=MXC_F_GCR_CLKCN_HIRC_EN; // Check if 96MHz clock is ready if (SYS_Clock_Timeout(MXC_F_GCR_CLKCN_HIRC_RDY) != E_NO_ERROR) { return E_TIME_OUT; } // Set 96MHz clock as System Clock MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, MXC_S_GCR_CLKCN_CLKSEL_HIRC); break; default: return E_BAD_PARAM; } // Wait for system clock to be ready if (SYS_Clock_Timeout(MXC_F_GCR_CLKCN_CKRDY) != E_NO_ERROR) { // Restore the old system clock if timeout MXC_SETFIELD(MXC_GCR->clkcn, MXC_F_GCR_CLKCN_CLKSEL, current_clock); return E_TIME_OUT; } // Disable other clocks switch(clock) { case SYS_CLOCK_NANORING: MXC_GCR->clkcn &= ~(MXC_F_GCR_CLKCN_HIRC_EN); break; case SYS_CLOCK_HFXIN: MXC_GCR->clkcn &= ~(MXC_F_GCR_CLKCN_HIRC_EN); break; case SYS_CLOCK_HFXIN_DIGITAL: MXC_GCR->clkcn &= ~(MXC_F_GCR_CLKCN_HIRC_EN); break; case SYS_CLOCK_HIRC: //Don't disable 32KHz clock break; } // Update the system core clock SystemCoreClockUpdate(); // Get the clock divider div = (MXC_GCR->clkcn & MXC_F_GCR_CLKCN_PSC) >> MXC_F_GCR_CLKCN_PSC_POS; //get ovr setting ovr = (MXC_PWRSEQ->lp_ctrl & MXC_F_PWRSEQ_LP_CTRL_OVR); //Set flash wait settings if(ovr == MXC_S_PWRSEQ_LP_CTRL_OVR_0_9V){ if(div == 0){ MXC_GCR->memckcn = (MXC_GCR->memckcn & ~(MXC_F_GCR_MEMCKCN_FWS)) | (0x2UL << MXC_F_GCR_MEMCKCN_FWS_POS); } else{ MXC_GCR->memckcn = (MXC_GCR->memckcn & ~(MXC_F_GCR_MEMCKCN_FWS)) | (0x1UL << MXC_F_GCR_MEMCKCN_FWS_POS); } } else if( ovr == MXC_S_PWRSEQ_LP_CTRL_OVR_1_0V){ if(div == 0){ MXC_GCR->memckcn = (MXC_GCR->memckcn & ~(MXC_F_GCR_MEMCKCN_FWS)) | (0x2UL << MXC_F_GCR_MEMCKCN_FWS_POS); } else{ MXC_GCR->memckcn = (MXC_GCR->memckcn & ~(MXC_F_GCR_MEMCKCN_FWS)) | (0x1UL << MXC_F_GCR_MEMCKCN_FWS_POS); } } else { if(div == 0){ MXC_GCR->memckcn = (MXC_GCR->memckcn & ~(MXC_F_GCR_MEMCKCN_FWS)) | (0x4UL << MXC_F_GCR_MEMCKCN_FWS_POS); } else if(div == 1){ MXC_GCR->memckcn = (MXC_GCR->memckcn & ~(MXC_F_GCR_MEMCKCN_FWS)) | (0x2UL << MXC_F_GCR_MEMCKCN_FWS_POS); } else{ MXC_GCR->memckcn = (MXC_GCR->memckcn & ~(MXC_F_GCR_MEMCKCN_FWS)) | (0x1UL << MXC_F_GCR_MEMCKCN_FWS_POS); } } return E_NO_ERROR; } /* ************************************************************************ */ int SYS_ClockEnable_X32K(sys_cfg_rtc_t *sys_cfg) { // Enable 32k Oscillator MXC_GCR->clkcn |=MXC_F_GCR_CLKCN_X32K_EN; return E_NO_ERROR; } /* ************************************************************************ */ int SYS_ClockDisable_X32K() { // Disable 32k Oscillator MXC_GCR->clkcn &= (~MXC_F_GCR_CLKCN_X32K_EN); return E_NO_ERROR; } /* ************************************************************************ */ int SYS_UART_Init(mxc_uart_regs_t *uart, const sys_cfg_uart_t* sys_cfg) { // Configure GPIO for UART if (uart == MXC_UART0) { SYS_ClockEnable(SYS_PERIPH_CLOCK_UART0); if(sys_cfg->map == MAP_A){ GPIO_Config(&gpio_cfg_uart0a); } else{ return E_BAD_PARAM; } if(sys_cfg->flow_flag == UART_FLOW_ENABLE){ GPIO_Config(&gpio_cfg_uart0rtscts); } } if (uart == MXC_UART1) { SYS_ClockEnable(SYS_PERIPH_CLOCK_UART1); if(sys_cfg->map == MAP_A){ GPIO_Config(&gpio_cfg_uart1a); } else if(sys_cfg->map == MAP_B){ GPIO_Config(&gpio_cfg_uart1b); } else if(sys_cfg->map == MAP_C){ GPIO_Config(&gpio_cfg_uart1c); } else{ return E_BAD_PARAM; } if(sys_cfg->flow_flag == UART_FLOW_ENABLE){ GPIO_Config(&gpio_cfg_uart1rtscts); } } return E_NO_ERROR; } /* ************************************************************************ */ int SYS_UART_Shutdown(mxc_uart_regs_t *uart) { if (uart == MXC_UART0) { SYS_ClockDisable(SYS_PERIPH_CLOCK_UART0); } else if (uart == MXC_UART1) { SYS_ClockDisable(SYS_PERIPH_CLOCK_UART1); } return E_NO_ERROR; } /* ************************************************************************ */ int SYS_I2C_Init(mxc_i2c_regs_t *i2c, const sys_cfg_i2c_t* sys_cfg) { // Configure GPIO for I2C if (i2c == MXC_I2C0) { SYS_ClockEnable(SYS_PERIPH_CLOCK_I2C0); GPIO_Config(&gpio_cfg_i2c0); } else if (i2c == MXC_I2C1) { SYS_ClockEnable(SYS_PERIPH_CLOCK_I2C1); GPIO_Config(&gpio_cfg_i2c1); } else { return E_NO_DEVICE; } return E_NO_ERROR; } /* ************************************************************************ */ int SYS_I2C_Shutdown(mxc_i2c_regs_t *i2c) { if (i2c == MXC_I2C0) { gpio_cfg_t cfg = { gpio_cfg_i2c0.port, gpio_cfg_i2c0.mask, GPIO_FUNC_IN, GPIO_PAD_NONE }; SYS_ClockDisable(SYS_PERIPH_CLOCK_I2C0); GPIO_Config(&cfg); } else if (i2c == MXC_I2C1) { gpio_cfg_t cfg = { gpio_cfg_i2c1.port, gpio_cfg_i2c1.mask, GPIO_FUNC_IN, GPIO_PAD_NONE }; SYS_ClockDisable(SYS_PERIPH_CLOCK_I2C1); GPIO_Config(&cfg); } else { return E_NO_DEVICE; } // Clear registers i2c->ctrl = 0; return E_NO_ERROR; } /* ************************************************************************ */ int SYS_DMA_Init(void) { SYS_ClockEnable(SYS_PERIPH_CLOCK_DMA); return E_NO_ERROR; } /* ************************************************************************ */ int SYS_DMA_Shutdown(void) { SYS_ClockDisable(SYS_PERIPH_CLOCK_DMA); return E_NO_ERROR; } /* ************************************************************************ */ unsigned SYS_I2C_GetFreq(mxc_i2c_regs_t *i2c) { return PeripheralClock; } /* ************************************************************************ */ unsigned SYS_TMR_GetFreq(mxc_tmr_regs_t *tmr) { return PeripheralClock; } /* ************************************************************************ */ void SYS_Reset0(sys_reset0_t reset) { MXC_GCR->rstr0 = reset; while(MXC_GCR->rstr0 != 0x0) {} } /* ************************************************************************ */ void SYS_Reset1(sys_reset1_t reset) { MXC_GCR->rstr1 = reset; while(MXC_GCR->rstr0 != 0x0) {} } /* ************************************************************************ */ void SYS_ClockDisable(sys_periph_clock_t clock) { /* The sys_periph_clock_t enum uses bit 27 (an unused bit in both perkcn registers) to determine which of the two perckcn registers to write to. */ if (clock & (1<<27)) { clock &= ~(1<<27); MXC_GCR->perckcn1 |= clock; } else { MXC_GCR->perckcn0 |= clock; } } /* ************************************************************************ */ void SYS_ClockEnable(sys_periph_clock_t clock) { /* The sys_periph_clock_t enum uses bit 27 (an unused bit in both perkcn registers) to determine which of the two perckcn registers to write to. */ if (clock & (1<<27)) { clock &= ~(1<<27); MXC_GCR->perckcn1 &= ~(clock); } else { MXC_GCR->perckcn0 &= ~(clock); } } /* ************************************************************************ */ #if defined (__ICCARM__) #pragma optimize=none /* Turn off optimizations for next function */ #elif defined ( __CC_ARM ) /* Keil MDK - Turn off optimizations after saving current state */ #pragma push /* Save current optimization level */ #pragma O0 /* Optimization level 0 */ #elif ( __GNUC__ ) /* GCC - Turn off optimizations after saving current state */ #pragma GCC push_options /* Save current optimization level */ #pragma GCC optimize ("O0") /* Set optimization level to none for this function */ #endif void SYS_Flash_Operation(void) { volatile uint32_t *line_addr; volatile uint32_t __attribute__ ((unused)) line; // Clear the cache MXC_ICC->cache_ctrl ^= MXC_F_ICC_CACHE_CTRL_CACHE_EN; MXC_ICC->cache_ctrl ^= MXC_F_ICC_CACHE_CTRL_CACHE_EN; // Clear the line fill buffer line_addr = (uint32_t*)(MXC_FLASH_MEM_BASE); line = *line_addr; line_addr = (uint32_t*)(MXC_FLASH_MEM_BASE + MXC_FLASH_PAGE_SIZE); line = *line_addr; } /* Set optimizations to the previous level. For IAR, the optimize none applies only to the next function. Keil MDK and GNUC need state restored. */ #if defined ( __CC_ARM ) #pragma pop /* Restore Kiel MDK optimizations to saved level */ #elif defined ( __GNUC__ ) #pragma GCC pop_options /* Restore GCC optimization level */ #endif /* ************************************************************************ */ int SYS_TMR_Init(mxc_tmr_regs_t *tmr, const sys_cfg_tmr_t* sys_cfg) { if(sys_cfg) { if(sys_cfg->out_en) { if (tmr == MXC_TMR0) { GPIO_Config(&gpio_cfg_tmr0); } } } if (tmr == MXC_TMR0) { SYS_ClockEnable(SYS_PERIPH_CLOCK_T0); } else if (tmr == MXC_TMR1) { SYS_ClockEnable(SYS_PERIPH_CLOCK_T1); } else if (tmr == MXC_TMR2) { SYS_ClockEnable(SYS_PERIPH_CLOCK_T2); } return E_NO_ERROR; } /* ************************************************************************ */ int SYS_FLC_Init(const sys_cfg_flc_t* sys_cfg) { return E_NO_ERROR; } /* ************************************************************************ */ int SYS_FLC_Shutdown(void) { return E_NO_ERROR; } /* ************************************************************************ */ int SYS_SPI17Y_Init(mxc_spi17y_regs_t *spi, const sys_cfg_spi17y_t* sys_cfg) { // Configure GPIO for spi17y if (spi == MXC_SPI17Y) { SYS_ClockEnable(SYS_PERIPH_CLOCK_SPI17Y); if(sys_cfg->map == MAP_A){ GPIO_Config(&gpio_cfg_spi17y); MXC_GPIO0->ds |= 0x0003BF0; }else{ return E_BAD_PARAM; } } else { return E_NO_DEVICE; } return E_NO_ERROR; } /* ************************************************************************ */ int SYS_SPI17Y_Shutdown(mxc_spi17y_regs_t *spi) { if (spi == MXC_SPI17Y) { SYS_ClockDisable(SYS_PERIPH_CLOCK_SPI17Y); } return E_NO_ERROR; } /* ************************************************************************ */ int SYS_SPIMSS_Init(mxc_spimss_regs_t *spi, const sys_cfg_spimss_t* sys_cfg) { // Configure GPIO for spimss if (spi == MXC_SPIMSS) { SYS_ClockEnable(SYS_PERIPH_CLOCK_SPIMSS); if(sys_cfg->map == MAP_A){ GPIO_Config(&gpio_cfg_spimss1a); // SPI1A chosen }else if(sys_cfg->map == MAP_B){ GPIO_Config(&gpio_cfg_spimss1b); // SPI1B chosen }else{ return E_BAD_PARAM; } } else { return E_NO_DEVICE; } return E_NO_ERROR; } /* ************************************************************************ */ int SYS_SPIMSS_Shutdown(mxc_spimss_regs_t *spi) { if(spi == MXC_SPIMSS) { SYS_ClockDisable(SYS_PERIPH_CLOCK_SPIMSS); } return E_NO_ERROR; } int SYS_TMR_Shutdown(mxc_tmr_regs_t *tmr) { return E_NO_ERROR; } /* ************************************************************************ */ int SYS_I2S_Init(const sys_cfg_i2s_t* sys_cfg) { if(sys_cfg->map == MAP_A) { GPIO_Config(&gpio_cfg_i2s1a); } else if(sys_cfg->map == MAP_B) { GPIO_Config(&gpio_cfg_i2s1b); } else { return E_BAD_PARAM; } SYS_ClockEnable(SYS_PERIPH_CLOCK_SPIMSS); return E_NO_ERROR; } /* ************************************************************************ */ int SYS_I2S_Shutdown(void) { SYS_ClockDisable(SYS_PERIPH_CLOCK_SPIMSS); return E_NO_ERROR; } /* ************************************************************************ */ int SYS_I2S_GetFreq(mxc_spimss_regs_t *spimss) { return PeripheralClock; } /* ************************************************************************ */ int SYS_RTC_SqwavInit(const sys_cfg_rtc_t* sys_cfg) { GPIO_Config(&gpio_cfg_rtc); return E_NO_ERROR; } /* ************************************************************************ */ uint32_t SYS_SysTick_GetFreq(void) { // Determine is using internal (SystemCoreClock) or external (32768) clock if ( (SysTick->CTRL & SysTick_CTRL_CLKSOURCE_Msk) || !(SysTick->CTRL & SysTick_CTRL_ENABLE_Msk)) { return SystemCoreClock; } else { return SYS_RTC_CLK; } } /* ************************************************************************ */ int SYS_SysTick_Config(uint32_t ticks, int clk_src, mxc_tmr_regs_t* tmr) { if(ticks == 0) return E_BAD_PARAM; // If SystemClock, call default CMSIS config and return if (clk_src) { return SysTick_Config(ticks); } else { /* External clock source requested enable RTC clock in run mode*/ RTC_Init(MXC_RTC, 0, 0, NULL); RTC_EnableRTCE(MXC_RTC); // Disable SysTick Timer SysTick->CTRL = 0; // Check reload value for valid if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) { // Reload value impossible return E_BAD_PARAM; } // set reload register SysTick->LOAD = ticks - 1; // set Priority for Systick Interrupt NVIC_SetPriority(SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); // Load the SysTick Counter Value SysTick->VAL = 0; // Enable SysTick IRQ and SysTick Timer leaving clock source as external SysTick->CTRL = SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; // Function successful return E_NO_ERROR; } } /* ************************************************************************ */ void SYS_SysTick_Disable(void) { SysTick->CTRL = 0; } /* ************************************************************************ */ int SYS_SysTick_Delay(uint32_t ticks) { uint32_t cur_ticks, num_full, num_remain, previous_ticks, num_subtract, i; uint32_t reload, value, ctrl; // save/restore variables if(ticks == 0) return E_BAD_PARAM; // If SysTick is not enabled we can take it for our delay if (!(SysTick->CTRL & SysTick_CTRL_ENABLE_Msk)) { // Save current state in case it's disabled but already configured, restore at return. reload = SysTick->LOAD; value = SysTick->VAL; ctrl = SysTick->CTRL; // get the number of ticks less than max RELOAD. num_remain = ticks % SysTick_LOAD_RELOAD_Msk; /* if ticks is < Max SysTick Reload num_full will be 0, otherwise it will give us the number of max SysTicks cycles required */ num_full = (ticks - 1) / SysTick_LOAD_RELOAD_Msk; // Do the required full systick countdowns if (num_full) { // load the max count value into systick SysTick->LOAD = SysTick_LOAD_RELOAD_Msk; // load the starting value SysTick->VAL = 0; // enable SysTick counter with SystemClock source internal, immediately forces LOAD register into VAL register SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; // CountFlag will get set when VAL reaches zero for (i = num_full; i > 0; i--) { do { cur_ticks = SysTick->CTRL; } while (!(cur_ticks & SysTick_CTRL_COUNTFLAG_Msk)); } // Disable systick SysTick->CTRL = 0; } // Now handle the remainder of ticks if (num_remain) { SysTick->LOAD = num_remain; SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; // wait for countflag to get set do { cur_ticks = SysTick->CTRL; } while (!(cur_ticks & SysTick_CTRL_COUNTFLAG_Msk)); // Disable systick SysTick->CTRL = 0; } // restore original state of SysTick and return SysTick->LOAD = reload; SysTick->VAL = value; SysTick->CTRL = ctrl; return E_NO_ERROR; } else { /* SysTick is enabled When SysTick is enabled count flag can not be used and the reload can not be changed. Do not read the CTRL register -> clears count flag */ // Get the reload value for wrap/reload case reload = SysTick->LOAD; // Read the starting systick value previous_ticks = SysTick->VAL; do { // get current SysTick value cur_ticks = SysTick->VAL; // Check for wrap/reload of timer countval if (cur_ticks > previous_ticks) { // subtract count to 0 (previous_ticks) and wrap (reload value - cur_ticks) num_subtract = (previous_ticks + (reload - cur_ticks)); } else { /* standard case (no wrap) subtract off the number of ticks since last pass */ num_subtract = (previous_ticks - cur_ticks); } // check to see if we are done. if (num_subtract >= ticks) return E_NO_ERROR; else ticks -= num_subtract; // cur_ticks becomes previous_ticks for next timer read. previous_ticks = cur_ticks; } while (ticks > 0); // Should not ever be reached return E_NO_ERROR; } } /* ************************************************************************ */ void SYS_SysTick_DelayUs(uint32_t us) { SYS_SysTick_Delay((uint32_t)(((uint64_t)SYS_SysTick_GetFreq() * us) / 1000000)); } /* ************************************************************************ */ int SYS_WDT_Init(mxc_wdt_regs_t* wdt, const sys_cfg_wdt_t* sys_cfg) { return E_NO_ERROR; } /**@} end of ingroup MXC_sys*/