/****************************************************************************** * * Copyright (C) 2022-2023 Maxim Integrated Products, Inc. (now owned by * Analog Devices, Inc.), * Copyright (C) 2023-2024 Analog Devices, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ /** * @file mxc_sys.c * @brief System layer driver. * @details This driver is used to control the system layer of the device. */ /* **** Includes **** */ #include #include "mxc_device.h" #include "mxc_assert.h" #include "mxc_sys.h" #include "mxc_delay.h" #include "gcr_regs.h" #include "fcr_regs.h" #include "mcr_regs.h" /** * @ingroup mxc_sys * @{ */ /* **** Definitions **** */ #define MXC_SYS_CLOCK_TIMEOUT MSEC(160) /* **** Globals **** */ /* **** Functions **** */ /* ************************************************************************** */ int MXC_SYS_IsClockEnabled(mxc_sys_periph_clock_t clock) { /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 for the pclkdis1 register. */ if (clock > 31) { clock -= 32; return !(MXC_GCR->pclkdis1 & (0x1 << clock)); } else { return !(MXC_GCR->pclkdis0 & (0x1 << clock)); } } /* ************************************************************************** */ void MXC_SYS_ClockDisable(mxc_sys_periph_clock_t clock) { /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 for the pclkdis1 register. */ if (clock > 31) { clock -= 32; MXC_GCR->pclkdis1 |= (0x1 << clock); } else { MXC_GCR->pclkdis0 |= (0x1 << clock); } } /* ************************************************************************** */ void MXC_SYS_ClockEnable(mxc_sys_periph_clock_t clock) { /* The mxc_sys_periph_clock_t enum uses enum values that are the offset by 32 for the pclkdis1 register. */ if (clock > 31) { clock -= 32; MXC_GCR->pclkdis1 &= ~(0x1 << clock); } else { MXC_GCR->pclkdis0 &= ~(0x1 << clock); } } /* ************************************************************************** */ void MXC_SYS_RTCClockEnable() { MXC_MCR->clkctrl &= ~MXC_F_MCR_CLKCTRL_ERTCO_PD; MXC_MCR->clkctrl |= MXC_F_MCR_CLKCTRL_ERTCO_EN; } /* ************************************************************************** */ int MXC_SYS_RTCClockDisable(void) { /* Check that the RTC is not the system clock source */ if ((MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL) != MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO) { MXC_MCR->clkctrl &= ~MXC_F_MCR_CLKCTRL_ERTCO_EN; return E_NO_ERROR; } else { return E_BAD_STATE; } } /******************************************************************************/ int MXC_SYS_ClockSourceEnable(mxc_sys_system_clock_t clock) { switch (clock) { case MXC_SYS_CLOCK_IPO: MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IPO_EN; return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IPO_RDY); break; case MXC_SYS_CLOCK_IBRO: MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN; return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IBRO_RDY); break; case MXC_SYS_CLOCK_ISO: MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ISO_EN; return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ISO_RDY); break; case MXC_SYS_CLOCK_INRO: // The nano ring clock is always enabled return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_INRO_RDY); break; case MXC_SYS_CLOCK_ERFO: MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERFO_EN; return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERFO_RDY); break; case MXC_SYS_CLOCK_ERTCO: MXC_MCR->clkctrl &= ~MXC_F_MCR_CLKCTRL_ERTCO_PD; MXC_MCR->clkctrl |= MXC_F_MCR_CLKCTRL_ERTCO_EN; return MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERTCO_RDY); break; default: return E_BAD_PARAM; break; } } /******************************************************************************/ int MXC_SYS_ClockSourceDisable(mxc_sys_system_clock_t clock) { uint32_t current_clock; current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL; // Don't turn off the clock we're running on if (clock == current_clock) { return E_BAD_PARAM; } switch (clock) { case MXC_SYS_CLOCK_IPO: MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_IPO_EN; break; case MXC_SYS_CLOCK_IBRO: MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_IBRO_EN; break; case MXC_SYS_CLOCK_ISO: MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_ISO_EN; break; case MXC_SYS_CLOCK_INRO: // The 8k clock is always enabled break; case MXC_SYS_CLOCK_ERFO: MXC_GCR->clkctrl &= ~MXC_F_GCR_CLKCTRL_ERFO_EN; break; case MXC_SYS_CLOCK_ERTCO: MXC_MCR->clkctrl &= ~MXC_F_MCR_CLKCTRL_ERTCO_EN; break; default: return E_BAD_PARAM; } return E_NO_ERROR; } /* ************************************************************************** */ int MXC_SYS_Clock_Timeout(uint32_t ready) { // Start timeout, wait for ready MXC_DelayAsync(MXC_SYS_CLOCK_TIMEOUT, NULL); do { if (MXC_GCR->clkctrl & ready) { MXC_DelayAbort(); return E_NO_ERROR; } } while (MXC_DelayCheck() == E_BUSY); return E_TIME_OUT; } /* ************************************************************************** */ int MXC_SYS_Clock_Select(mxc_sys_system_clock_t clock) { uint32_t current_clock; // Save the current system clock current_clock = MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL; switch (clock) { case MXC_SYS_CLOCK_IPO: // Enable IPO clock if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IPO_EN)) { MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IPO_EN; // Check if IPO clock is ready if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IPO_RDY) != E_NO_ERROR) { return E_TIME_OUT; } } // Set IPO clock as System Clock MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IPO); break; case MXC_SYS_CLOCK_IBRO: // Enable IBRO clock if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_IBRO_EN)) { MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_IBRO_EN; // Check if IBRO clock is ready if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_IBRO_RDY) != E_NO_ERROR) { return E_TIME_OUT; } } // Set IBRO clock as System Clock MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, MXC_S_GCR_CLKCTRL_SYSCLK_SEL_IBRO); break; case MXC_SYS_CLOCK_ISO: // Enable ISO clock if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_ISO_EN)) { MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ISO_EN; // Check if ISO clock is ready if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ISO_RDY) != E_NO_ERROR) { return E_TIME_OUT; } } // Set ISO clock as System Clock MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ISO); break; case MXC_SYS_CLOCK_ERFO: // Enable XRFO clock if (!(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_ERFO_EN)) { MXC_GCR->clkctrl |= MXC_F_GCR_CLKCTRL_ERFO_EN; // Check if XRFO clock is ready if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERFO_RDY) != E_NO_ERROR) { return E_TIME_OUT; } } // Set XFRO clock as System Clock MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERFO); break; case MXC_SYS_CLOCK_INRO: // Set INRO clock as System Clock MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, MXC_S_GCR_CLKCTRL_SYSCLK_SEL_INRO); break; case MXC_SYS_CLOCK_ERTCO: // Enable XRTCO clock if (!(MXC_MCR->clkctrl & MXC_F_MCR_CLKCTRL_ERTCO_EN)) { MXC_MCR->clkctrl &= ~MXC_F_MCR_CLKCTRL_ERTCO_PD; MXC_MCR->clkctrl |= MXC_F_MCR_CLKCTRL_ERTCO_EN; // Check if XRTCO clock is ready if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_ERTCO_RDY) != E_NO_ERROR) { return E_TIME_OUT; } } // Set XRTCO clock as System Clock MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, MXC_S_GCR_CLKCTRL_SYSCLK_SEL_ERTCO); break; default: return E_BAD_PARAM; } // Wait for system clock to be ready if (MXC_SYS_Clock_Timeout(MXC_F_GCR_CLKCTRL_SYSCLK_RDY) != E_NO_ERROR) { // Restore the old system clock if timeout MXC_SETFIELD(MXC_GCR->clkctrl, MXC_F_GCR_CLKCTRL_SYSCLK_SEL, current_clock); return E_TIME_OUT; } // Update the system core clock SystemCoreClockUpdate(); return E_NO_ERROR; } /* ************************************************************************** */ void MXC_SYS_Reset_Periph(mxc_sys_reset_t reset) { /* The mxc_sys_reset_t enum uses enum values that are the offset by 32 for the rst1 register. */ if (reset > 31) { reset -= 32; MXC_GCR->rst1 = (0x1 << reset); } else { MXC_GCR->rst0 = (0x1 << reset); } } /* ************************************************************************** */ int MXC_SYS_GetUSN(uint8_t *usn, uint8_t *checksum) { //FIXME: No flash for ME55 return E_NOT_SUPPORTED; // if (usn == NULL) { // return E_NOT_SUPPORTED; // } // uint32_t *infoblock = (uint32_t *)MXC_INFO0_MEM_BASE; // /* Read the USN from the info block */ // MXC_FLC_UnlockInfoBlock(MXC_INFO0_MEM_BASE); // memset(usn, 0, MXC_SYS_CHECKSUM_LEN); // usn[0] = (infoblock[0] & 0x007F8000) >> 15; // usn[1] = (infoblock[0] & 0x7F800000) >> 23; // usn[2] = (infoblock[1] & 0x0000007F) << 1; // usn[2] |= (infoblock[0] & 0x80000000) >> 31; // usn[3] = (infoblock[1] & 0x00007F80) >> 7; // usn[4] = (infoblock[1] & 0x007F8000) >> 15; // usn[5] = (infoblock[1] & 0x7F800000) >> 23; // usn[6] = (infoblock[2] & 0x007F8000) >> 15; // usn[7] = (infoblock[2] & 0x7F800000) >> 23; // usn[8] = (infoblock[3] & 0x0000007F) << 1; // usn[8] |= (infoblock[2] & 0x80000000) >> 31; // usn[9] = (infoblock[3] & 0x00007F80) >> 7; // usn[10] = (infoblock[3] & 0x007F8000) >> 15; // // Compute the checksum // if (checksum != NULL) { // uint32_t key[4]; // uint32_t pt32[4]; // uint32_t check_csum32[4]; // uint8_t check_csum[MXC_SYS_CHECKSUM_LEN]; // /* Initialize key and plaintext */ // memset(key, 0, MXC_SYS_CHECKSUM_LEN); // memset(pt32, 0, MXC_SYS_CHECKSUM_LEN); // memcpy(pt32, usn, MXC_SYS_CHECKSUM_LEN); // /* Read the checksum from the info block */ // checksum[0] = ((infoblock[3] & 0x7F800000) >> 23); // checksum[1] = ((infoblock[4] & 0x007F8000) >> 15); // MXC_CTB_Init(MXC_CTB_FEATURE_CIPHER); // /* Reset the CTB */ // MXC_CTB->ctrl = MXC_F_CTB_CTRL_RST; // /* Set the legacy bit */ // MXC_CTB->ctrl |= MXC_F_CTB_CTRL_FLAG_MODE; // /* Clear interrupt flags */ // MXC_CTB->ctrl |= MXC_F_CTB_CTRL_CPH_DONE; // /* Setup the key source */ // MXC_CTB->cipher_ctrl = MXC_S_CTB_CIPHER_CTRL_SRC_CIPHERKEY; // /* Setup the CT calculation */ // MXC_CTB->cipher_ctrl |= MXC_S_CTB_CIPHER_CTRL_CIPHER_AES128; // /* Load the key */ // MXC_CTB->cipher_key[0] = key[0]; // MXC_CTB->cipher_key[1] = key[1]; // MXC_CTB->cipher_key[2] = key[2]; // MXC_CTB->cipher_key[3] = key[3]; // /* Wait for the ready flag */ // while (!(MXC_CTB->ctrl & MXC_F_CTB_CTRL_RDY)) {} // /* Copy data to start the operation */ // MXC_CTB->din[0] = pt32[0]; // MXC_CTB->din[1] = pt32[1]; // MXC_CTB->din[2] = pt32[2]; // MXC_CTB->din[3] = pt32[3]; // /* Wait for and clear the done flag */ // while (!(MXC_CTB->ctrl & MXC_F_CTB_CTRL_CPH_DONE)) {} // MXC_CTB->ctrl |= MXC_F_CTB_CTRL_CPH_DONE; // /* Copy out the cipher text */ // check_csum32[0] = MXC_CTB->dout[0]; // check_csum32[1] = MXC_CTB->dout[1]; // check_csum32[2] = MXC_CTB->dout[2]; // check_csum32[3] = MXC_CTB->dout[3]; // memcpy(check_csum, check_csum32, MXC_SYS_CHECKSUM_LEN); // /* Verify the checksum */ // if ((checksum[1] != check_csum[0]) || (checksum[0] != check_csum[1])) { // MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE); // return E_UNKNOWN; // } // } // /* Add the info block checksum to the USN */ // usn[11] = ((infoblock[3] & 0x7F800000) >> 23); // usn[12] = ((infoblock[4] & 0x007F8000) >> 15); // MXC_FLC_LockInfoBlock(MXC_INFO0_MEM_BASE); // return E_NO_ERROR; } /* ************************************************************************** */ uint8_t MXC_SYS_GetRev(void) { //FIXME: Same as above^^^ return E_NOT_SUPPORTED; // uint8_t serialNumber[13]; // MXC_SYS_GetUSN(serialNumber, 13); <<<<<<<< // if ((serialNumber[0] < 0x9F) | ((serialNumber[0] & 0x0F) > 0x09)) { // // Fail back to the hardware register // return MXC_GCR->revision & 0xFF; // } // return serialNumber[0]; } /* ************************************************************************** */ uint32_t MXC_SYS_RiscVClockRate(void) { // // If in LPM mode and the PCLK is selected as the RV32 clock source, // if(((MXC_GCR->pm & MXC_F_GCR_PM_MODE) == MXC_S_GCR_PM_MODE_LPM) && // (MXC_PWRSEQ->lpcn & MXC_F_PWRSEQ_LPCN_LPMCLKSEL)) { // return ISO_FREQ; // } else { // return PeripheralClock; return SystemCoreClock; // } } /**@} end of mxc_sys */