/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 2019 embedded brains GmbH * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include BSP_FAST_TEXT_SECTION static void pmc_wait_for_master_clock_ready(volatile Pmc *pmc) { while ((pmc->PMC_SR & PMC_SR_MCKRDY) == 0) { /* Wait */ } } BSP_FAST_TEXT_SECTION static void pmc_set_master_clock_source(volatile Pmc *pmc, uint32_t mckr) { pmc->PMC_MCKR = (pmc->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | (mckr & PMC_MCKR_CSS_Msk); } BSP_FAST_TEXT_SECTION static void pmc_set_master_clock_prescaler(volatile Pmc *pmc, uint32_t mckr) { pmc->PMC_MCKR = (pmc->PMC_MCKR & ~PMC_MCKR_PRES_Msk) | (mckr & PMC_MCKR_PRES_Msk); } BSP_FAST_TEXT_SECTION static void pmc_set_master_clock_division(volatile Pmc *pmc, uint32_t mckr) { pmc->PMC_MCKR = (pmc->PMC_MCKR & ~PMC_MCKR_MDIV_Msk) | (mckr & PMC_MCKR_MDIV_Msk); } BSP_FAST_TEXT_SECTION static void pmc_wait_for_main_rc_osc(volatile Pmc *pmc) { while ((pmc->PMC_SR & PMC_SR_MOSCRCS) == 0) { /* Wait */ } } BSP_FAST_TEXT_SECTION static void pmc_wait_for_main_osc_selection(volatile Pmc *pmc) { while ((pmc->PMC_SR & PMC_SR_MOSCSELS) == 0) { /* Wait */ } } BSP_FAST_TEXT_SECTION static void pmc_use_main_rc_osc_4mhz(volatile Pmc *pmc) { uint32_t ckgr_mor; ckgr_mor = pmc->CKGR_MOR; ckgr_mor |= CKGR_MOR_KEY_PASSWD; /* Enable main RC oscillator */ ckgr_mor |= CKGR_MOR_MOSCRCEN; PMC->CKGR_MOR = ckgr_mor; pmc_wait_for_main_rc_osc(pmc); /* Set main RC oscillator frequency to 4MHz */ ckgr_mor &= ~CKGR_MOR_MOSCRCF_Msk; ckgr_mor |= CKGR_MOR_MOSCRCF_4_MHz; pmc->CKGR_MOR = ckgr_mor; pmc_wait_for_main_rc_osc(pmc); /* Switch to main RC oscillator */ ckgr_mor &= ~CKGR_MOR_MOSCSEL; pmc->CKGR_MOR = ckgr_mor; pmc_wait_for_main_osc_selection(pmc); pmc_wait_for_master_clock_ready(pmc); } BSP_FAST_TEXT_SECTION static void pmc_use_main_ext_osc(volatile Pmc *pmc) { uint32_t ckgr_mor; ckgr_mor = pmc->CKGR_MOR; ckgr_mor |= CKGR_MOR_KEY_PASSWD; /* Enable main external oscillator */ ckgr_mor |= CKGR_MOR_MOSCRCF_12_MHz | CKGR_MOR_MOSCXTEN | CKGR_MOR_MOSCRCEN | CKGR_MOR_MOSCXTST(DEFAUTL_MAIN_OSC_COUNT); PMC->CKGR_MOR = ckgr_mor; pmc_wait_for_main_rc_osc(pmc); pmc_wait_for_master_clock_ready(pmc); /* Switch to main external oscillator */ ckgr_mor |= CKGR_MOR_MOSCSEL; pmc->CKGR_MOR = ckgr_mor; pmc_wait_for_main_osc_selection(pmc); pmc_wait_for_master_clock_ready(pmc); } BSP_FAST_TEXT_SECTION void atsam_power_handler_wait_mode( const atsam_power_control *control, atsam_power_state state ) { rtems_interrupt_level level; volatile Pmc *pmc; uint32_t mckr; uint32_t fmr; uint32_t fsmr; (void) control; pmc = PMC; switch (state) { case ATSAM_POWER_OFF: rtems_interrupt_disable(level); mckr = pmc->PMC_MCKR; /* Switch main clock to main RC oscillator (4MHz) */ pmc_use_main_rc_osc_4mhz(pmc); pmc_set_master_clock_source(pmc, PMC_MCKR_CSS_MAIN_CLK); pmc_wait_for_master_clock_ready(pmc); pmc_set_master_clock_prescaler(pmc, PMC_MCKR_PRES_CLK_1); pmc_wait_for_master_clock_ready(pmc); pmc_set_master_clock_division(pmc, PMC_MCKR_MDIV_EQ_PCK); pmc_wait_for_master_clock_ready(pmc); /* Set flash wait state to 0 */ fmr = EFC->EEFC_FMR; EFC->EEFC_FMR = EEFC_FMR_FWS(0); /* Enter wait mode */ fsmr = pmc->PMC_FSMR; pmc->PMC_FSMR = fsmr | PMC_FSMR_LPM; pmc->CKGR_MOR = CKGR_MOR_KEY_PASSWD | CKGR_MOR_WAITMODE; pmc_wait_for_master_clock_ready(pmc); __asm__ volatile ( "wfe" : : : "memory" ); pmc->PMC_FSMR = fsmr; /* Restore main clock */ pmc_set_master_clock_prescaler(pmc, mckr); pmc_wait_for_master_clock_ready(pmc); pmc_set_master_clock_division(pmc, mckr); pmc_wait_for_master_clock_ready(pmc); pmc_set_master_clock_source(pmc, mckr); pmc_wait_for_master_clock_ready(pmc); pmc_use_main_ext_osc(pmc); /* Restore flash wait state */ EFC->EEFC_FMR = fmr; rtems_interrupt_enable(level); break; case ATSAM_POWER_INIT: rtems_interrupt_disable(level); pmc->PMC_FSMR = (pmc->PMC_FSMR & ~PMC_FSMR_FLPM_Msk) | PMC_FSMR_FLPM(PMC_FSMR_FLPM_FLASH_DEEP_POWERDOWN); /* No Cortex-M7 Deep Sleep mode (Backup Mode) */ SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; rtems_interrupt_enable(level); default: break; } }