From 253a09193623460374591b3fee3ba08aeba7df7e Mon Sep 17 00:00:00 2001 From: Kevin Stefanik Date: Mon, 30 Mar 2020 15:41:48 -0400 Subject: [PATCH] pac55xx: adding memctl for flash/sram access, and clock/pll configuration functions. Merge-conflict: took _prior_ verision of CCS_MUXSELR_MASK_PIN as bracketing of (pin) seemed more correct! --- include/libopencm3/pac55xx/ccs.h | 246 +++++++++++++++++++++++++- include/libopencm3/pac55xx/memctl.h | 163 +++++++++++++++++ lib/pac55xx/Makefile | 2 + lib/pac55xx/ccs.c | 261 ++++++++++++++++++++++++++++ lib/pac55xx/memctl.c | 78 +++++++++ 5 files changed, 744 insertions(+), 6 deletions(-) create mode 100644 include/libopencm3/pac55xx/memctl.h create mode 100644 lib/pac55xx/ccs.c create mode 100644 lib/pac55xx/memctl.c diff --git a/include/libopencm3/pac55xx/ccs.h b/include/libopencm3/pac55xx/ccs.h index b3054f1d..b18f2f1c 100644 --- a/include/libopencm3/pac55xx/ccs.h +++ b/include/libopencm3/pac55xx/ccs.h @@ -4,6 +4,7 @@ * @defgroup system_defines Clock Config and System Defines * @ingroup PAC55xx_defines * @author Brian Viele + * @author Kevin Stefanik * LGPL License Terms @ref lgpl_license * @date 1 Dec 2019 * @@ -26,18 +27,95 @@ * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ -#ifndef INCLUDE_LIBOPENCM3_PAC55XX_CCS_H_ -#define INCLUDE_LIBOPENCM3_PAC55XX_CCS_H_ +#ifndef LIBOPENCM3_PAC55XX_CCS_H_ +#define LIBOPENCM3_PAC55XX_CCS_H_ #include +#include +#include +#include /**@{*/ -/** Clock Control Registers - * @defgroup clock_config_regs Clock Config Registers. - * @{*/ +/** @defgroup ccs_frequencies CCS Frequencies +@{*/ +/** Ring Oscillator Frequency */ +#define CCS_ROSC_FREQ (16000000U) +/** Internally generated and trimmed 4MHz clock */ +#define CCS_CLKREF_FREQ ( 4000000U) +/** Maximum external clock frequency */ +#define CCS_EXTCLK_MAX_FREQ (20000000U) +/**@}*/ + +/** @defgroup ccs_ctl_reg Clock Control Register +@{*/ #define CCSCTL MMIO32(SCC_BASE) +#define CCS_CTL_FRCLKMUXSEL_MASK (0x03) +#define CCS_CTL_FRCLKMUXSEL(sel) ((sel) & CCS_CTL_FRCLKMUXSEL_MASK) +#define CCS_CTL_FRCLKMUXSEL_ROSC (0) +#define CCS_CTL_FRCLKMUXSEL_CLKREF (1) +#define CCS_CTL_FRCLKMUXSEL_EXTCLK (3) +#define CCS_CTL_ROSCEN BIT2 +#define CCS_CTL_SCLKMUXSEL BIT4 +#define CCS_CTL_SCLKMUXSEL_FRCLK (0) +#define CCS_CTL_SCLKMUXSEL_PLLCLK (1) +#define CCS_CTL_CLKFAILEN BIT5 +#define CCS_CTL_CLKFAILMUXSEL BIT6 +#define CCS_CTL_CLKFAILIF BIT7 +#define CCS_CTL_LDOEN BIT8 +#define CCS_CTL_SWRESET BIT11 +#define CCS_CTL_PCLKEN BIT12 +#define CCS_CTL_ACLKEN BIT13 +#define CCS_CTL_ADCCLKEN BIT14 +#define CCS_CTL_STCLKSLPEN BIT15 +#define CCS_CTL_PCLKDIV_MASK (0x07) +#define CCS_CTL_PCLKDIV_SHIFT (16) +/* Supported PCLK divisors: 1-8 */ +#define CCS_CTL_PCLKDIV(div) (((div-1) & CCS_CTL_PCLKDIV_MASK) << CCS_CTL_PCLKDIV_SHIFT) +#define CCS_CTL_ACLKDIV_MASK (0x07) +#define CCS_CTL_ACLKDIV_SHIFT (20) +/* Supported ACLK divisors: 1-8 */ +#define CCS_CTL_ACLKDIV(div) (((div-1) & CCS_CTL_ACLKDIV_MASK) << CCS_CTL_ACLKDIV_SHIFT) +#define CCS_CTL_HCLKDIV_MASK (0x07) +#define CCS_CTL_HCLKDIV_SHIFT (24) +/* Supported HCLK divisors: 1-8 */ +#define CCS_CTL_HCLKDIV(div) (((div-1) & CCS_CTL_HCLKDIV_MASK) << CCS_CTL_HCLKDIV_SHIFT) +#define CCS_CTL_USAMODE BIT28 +#define CCS_CTL_USBMODE BIT29 +#define CCS_CTL_USCMODE BIT30 +#define CCS_CTL_USDMODE BIT31 +/**@}*/ + +/** @defgroup ccs_pllctl_reg CCS PLL Control Register +@{*/ #define CCSPLLCTL MMIO32(SCC_BASE + 0x04) +/** PLL Enable */ +#define CCS_PLLCTL_PLLEN BIT0 +/** PLL Bypass */ +#define CCS_PLLCTL_PLLBP BIT1 +#define CCS_PLLCTL_PLLOUTDIV_MASK (0x03) +#define CCS_PLLCTL_PLLOUTDIV_SHIFT (2) +/** PLL Output Divisor */ +#define CCS_PLLCTL_PLLOUTDIV(div) (((div) & CCS_PLLCTL_PLLOUTDIV_MASK) << CCS_PLLCTL_PLLOUTDIV_SHIFT) +#define CCS_PLLCTL_PLLOUTDIV1 (0) +#define CCS_PLLCTL_PLLOUTDIV2 (1) +#define CCS_PLLCTL_PLLOUTDIV4 (2) +#define CCS_PLLCTL_PLLOUTDIV8 (3) +#define CCS_PLLCTL_PLLINDIV_MASK (0x0F) +#define CCS_PLLCTL_PLLINDIV_SHIFT (4) +/** PLL Input Divisor */ +#define CCS_PLLCTL_PLLINDIV(div) (((div) & CCS_PLLCTL_PLLINDIV_MASK) << CCS_PLLCTL_PLLINDIV_SHIFT) +#define CCS_PLLCTL_PLLFBDIV_MASK (0x3FFF) +#define CCS_PLLCTL_PLLFBDIV_SHIFT (8) +/** PLL Feedback Divisor */ +#define CCS_PLLCTL_PLLFBDIV(div) (((div) & CCS_PLLCTL_PLLFBDIV_MASK) << CCS_PLLCTL_PLLFBDIV_SHIFT) +/** PLL Lock */ +#define CCS_PLLCTL_PLLLOCK BIT24 +/**@}*/ + +/** @defgroup ccs_rosctrim Ring Oscillator Trim Control Register +@{*/ +#define CCSROSCTRIM_MASK (0x7F) #define CCSROSCTRIM MMIO32(SCC_BASE + 0x08) /**@}*/ @@ -136,6 +214,162 @@ typedef enum { CCS_DSR_DS_25MA = 0x07, } ccs_drive_strength_t; /**@}*/ + /**@}*/ -#endif /* INCLUDE_LIBOPENCM3_PAC55XX_CCS_H_ */ +BEGIN_DECLS + +/** + * @defgroup ccs_api Clock Control System API + * @ingroup peripheral_apis + * @brief PAC5xx CCS Driver + * @author @htmlonly © @endhtmlonly 2020 Kevin Stefanik + * @date March 7, 2020 + * + * This library supports the CCS module in the PAC55xx SoC from Qorvo. + * + * LGPL License Terms @ref lgpl_license + */ + +/**@{*/ + +/** + * Select the source for FRCLK. + * @param[in] sel one of: + * - /ref CCS_CTL_FRCLKMUXSEL_ROSC - 16MHz ring oscillator + * - /ref CCS_CTL_FRCLKMUXSEL_CLKREF - trimmed 4MHz clock + * - /ref CCS_CTL_FRCLKMUXSEL_EXTCLK + */ +void ccs_frclkmux_select(uint32_t sel); +/** Enable the 16MHz Ring oscillator */ +void ccs_rosc_enable(void); +/** Disable the 16MHz Ring oscillator */ +void ccs_rosc_disable(void); +/** Select FRCLK for SCLK */ +void ccs_sclkmux_select_frclk(void); +/** Select PLLCLK for SCLK */ +void ccs_sclkmux_select_pllclk(void); +/** Enable Clock Fail Detection */ +void ccs_clkfail_enable(void); +/** Disable Clock Fail Detection */ +void ccs_clkfail_disable(void); +/** Select FRCLK for Clock Fail Detection */ +void ccs_clkfailmux_select_frclk(void); +/** Select PLLCLK for Clock Fail Detection */ +void ccs_clkfailmux_select_pllclk(void); +/** Enable the LDO */ +void ccs_ldo_enable(void); +/** Disable the LDO */ +void ccs_ldo_disable(void); +/** Enable the Peripheral Clock */ +void ccs_pclk_enable(void); +/** Disable the Peripheral Clock */ +void ccs_pclk_disable(void); +/** Enable the Auxiliary Clock */ +void ccs_aclk_enable(void); +/** Disable the Auxiliary Clock */ +void ccs_aclk_disable(void); +/** Enable the ADC Clock */ +void ccs_adcclk_enable(void); +/** Disable the ADC Clock */ +void ccs_adcclk_disable(void); +/** Enable SysTick clock gating in deep sleep mode */ +void ccs_stclk_sleep_enable(void); +/** Disable SysTick clock gating in deep sleep mode */ +void ccs_stclk_sleep_disable(void); +/** + * Set the divisor for the Peripheral Clock. + * @param[in] div PCLK Divisor: 1-8. + */ +void ccs_set_pclkdiv(uint32_t div); +/** + * Set the divisor for the Auxiliary Clock. + * @param[in] div ACLK Divisor: 1-8. + */ +void ccs_set_aclkdiv(uint32_t div); +/** + * Set the divisor for the AHB Clock. + * @param[in] div HCLK Divisor: 1-8. + */ +void ccs_set_hclkdiv(uint32_t div); +/** Enable the PLL */ +void ccs_pll_enable(void); +/** Disable the PLL */ +void ccs_pll_disable(void); +/** Check if the PLL is locked. + * @return true if locked. + */ +bool ccs_pll_locked(void); +/** Enable the PLL bypass */ +void ccs_pll_bypass_enable(void); +/** Disable the PLL bypass */ +void ccs_pll_bypass_disable(void); +/** + * Set the output divisor. + * @param[in] div Output divisor, one of: + * - /ref CCS_PLLCTL_PLLOUTDIV1 + * - /ref CCS_PLLCTL_PLLOUTDIV2 + * - /ref CCS_PLLCTL_PLLOUTDIV4 + * - /ref CCS_PLLCTL_PLLOUTDIV8 + */ +void ccs_pll_set_outdiv(uint32_t div); +/** + * Set the PLL input divisor. + * @param[in] div Input divisor, 1-15. + */ +void ccs_pll_set_indiv(uint32_t div); +/** + * Set the PLL feedback divisor. + * @param[in] div Feedback divisor, 4-16383. + */ +void ccs_pll_set_fbdiv(uint32_t div); +/** + * Configure the CCS PLL, enable it, and wait for lock. + * @param[in] indiv Input divisor, 1-15. + * @param[in] fbdiv Feedback divisor, 4-16383. + * @param[in] outdiv Output divisor, one of: + * - /ref CCS_PLLCTL_PLLOUTDIV1 + * - /ref CCS_PLLCTL_PLLOUTDIV2 + * - /ref CCS_PLLCTL_PLLOUTDIV4 + * - /ref CCS_PLLCTL_PLLOUTDIV8 + */ +void css_pll_config_enable(uint32_t indiv, uint32_t fbdiv, uint32_t outdiv); +/** + * Get the clock rate (in Hz) of the specified peripheral. This will pull the + * proper sources out of the clock tree and calculate the clock for the + * peripheral for return to the user, based on current settings. + * @param[in] periph Peripheral base address to get the clock rate for. + * @param[in] select Peripheral-controlled clock select value. Set to 0 when not applicable. + * @return Clock rate in Hz for the specified peripheral. 0 if undefined or error. + */ +uint32_t ccs_get_peripheral_clk_freq(uint32_t periph, uint32_t select); +/** Restores CCSCTL and CCSPLLCTL registers to default/safe values */ +void ccs_reset_clocks(void); + +/** CCS Clock Configuration structure. */ +struct ccs_clk_config { + uint32_t frclk_source; /**< FRCLK source input selection */ + uint32_t extclk_frequency; /**< EXTCLK frequency, 0 if none. */ + uint32_t sclk_source; /**< SCLK source selection */ + uint32_t pll_indiv; /**< PLL Input Divider 1-15 */ + uint32_t pll_fbdiv; /**< PLL Feedback Divider 4-16383 */ + uint32_t pll_outdiv; /**< PLL Output Divider */ + uint32_t hclkdiv; /**< Divisor from SCLK to HCLK */ + uint32_t aclkdiv; /**< Divisor from SCLK to ACLK */ + uint32_t pclkdiv; /**< Divisor from HCLK to PCLK */ + uint32_t mem_wstate; /**< Number of Flash Read wait states */ + uint32_t mem_mclkdiv; /**< Divisor from HCLK to MCLK */ + bool mem_mclksel; /**< false: ROSCLK, true: HCLK/MCLK */ + bool mem_enable_cache; /**< false: disable cache, true: enable cache */ +}; +/** + * Setup the PAC55xx clocks with the given struct. + * @param[in] config CCS Clock configuration struct /ref ccs_clk_config + */ +void ccs_configure_clocks(const struct ccs_clk_config *config); + +/**@}*/ + +END_DECLS + +#endif /* LIBOPENCM3_PAC55XX_CCS_H_ */ diff --git a/include/libopencm3/pac55xx/memctl.h b/include/libopencm3/pac55xx/memctl.h new file mode 100644 index 00000000..1a70e45d --- /dev/null +++ b/include/libopencm3/pac55xx/memctl.h @@ -0,0 +1,163 @@ +/** + * @brief Memory Controller definitions for the Qorvo PAC55xx series of microcontrollers + * + * @addtogroup PAC55xx_memctl Memory Controller Defines + * @ingroup PAC55xx_defines + * @author Kevin Stefanik + * LGPL License Terms @ref lgpl_license + * @date 17 Mar 2020 + * + * Definitions in this file come from the PAC55XX Family User Guide Rev 1.23 + * by Active-Semi dated November 19, 2019. + */ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2020 Kevin Stefanik + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef LIBOPENCM3_PAC55XX_MEMCTL_H_ +#define LIBOPENCM3_PAC55XX_MEMCTL_H_ + +#include +#include +/**@{*/ + +/** @defgroup memctl_reg Memory Controller Configuration Register +@{*/ +/** Memory Controller Configuration Register */ +#define MEMCTL_MEMCTLR MMIO32(MEMCTL_BASE) +#define MEMCTL_MEMCTLR_WSTATE_MASK (0xF) +#define MEMCTL_MEMCTLR_WSTATE(ws) ((ws) & MEMCTL_MEMCTLR_WSTATE_MASK) +#define MEMCTL_MEMCTLR_MCLKDIV_MASK (0xF) +#define MEMCTL_MEMCTLR_MCLKDIV_SHIFT 4 +/* Supported MCLK divisors: 1-16 */ +#define MEMCTL_MEMCTLR_MCLKDIV(div) (((div-1) & MEMCTL_MEMCTLR_MCLKDIV_MASK) << MEMCTL_MEMCTLR_MCLKDIV_SHIFT) +#define MEMCTL_MEMCTLR_WRITEWORDCNT_MASK (0x3) +#define MEMCTL_MEMCTLR_WRITEWORDCNT_SHIFT 8 +#define MEMCTL_MEMCTLR_WRITEWORDCNT(cnt) (((cnt) & MEMCTL_MEMCTLR_WRITEWORDCNT_MASK) << MEMCTL_MEMCTLR_WRITEWORDCNT_SHIFT) +#define MEMCTL_MEMCTLR_SEIE BIT16 +#define MEMCTL_MEMCTLR_DEIE BIT17 +#define MEMCTL_MEMCTLR_INVADDRIE BIT18 +#define MEMCTL_MEMCTLR_STBY BIT19 +#define MEMCTL_MEMCTLR_ECCDIS BIT20 +#define MEMCTL_MEMCTLR_CACHEDIS BIT21 +#define MEMCTL_MEMCTLR_MCLKSEL BIT22 +/**@}*/ + +/** @defgroup memstatus_reg Memory Controller Status Register +@{*/ +/** Memory Controller Status Register */ +#define MEMCTL_MEMSTATUS MMIO32(MEMCTL_BASE + 0x0004) +#define MEMCTL_MEMSTATUS_WBUSY BIT0 +#define MEMCTL_MEMSTATUS_EBUSY BIT1 +#define MEMCTL_MEMSTATUS_WRITEWORDCNT_MASK (0x3) +#define MEMCTL_MEMSTATUS_WRITEWORDCNT_SHIFT 8 +#define MEMCTL_MEMSTATUS_WRITEWORDCNT ((MEMCTL_MEMSTATUS >> MEMCTL_MEMSTATUS_WRITEWORDCNT_SHIFT) & MEMCTL_MEMSTATUS_WRITEWORDCNT_MASK) +#define MEMCTL_MEMSTATUS_WRITEWORDCNT_4BYTES (0) +#define MEMCTL_MEMSTATUS_WRITEWORDCNT_8BYTES (1) +#define MEMCTL_MEMSTATUS_WRITEWORDCNT_12BYTES (2) +#define MEMCTL_MEMSTATUS_WRITEWORDCNT_16BYTES (3) +#define MEMCTL_MEMSTATUS_SE BIT16 +#define MEMCTL_MEMSTATUS_DE BIT17 +#define MEMCTL_MEMSTATUS_INVADDR BIT18 +/**@}*/ + +/** @defgroup flashlock_vals Flash Lock/Write Enable Register values +@{*/ +/** Flash Lock Access Register */ +#define MEMCTL_FLASHLOCK MMIO32(MEMCTL_BASE + 0x0008) +#define MEMCTL_FLASHLOCK_CLEAR (0) +#define MEMCTL_FLASHLOCK_ALLOW_FLASH_WRITE (0x43DF140A) +#define MEMCTL_FLASHLOCK_ALLOW_MEMCTL_WRITE (0xD513B490) +#define MEMCTL_FLASHLOCK_ALLOW_INFO2_SWDFUSE (0x79B4F762) +/**@}*/ + +/** Flash Page Address Register */ +#define MEMCTL_FLASHPAGE MMIO32(MEMCTL_BASE + 0x000C) +/** SWD Unlock Register */ +#define MEMCTL_SWDUNLOCK MMIO32(MEMCTL_BASE + 0x0010) + +/** @defgroup flasherase_vals Flash Erase Enable Register values +@{*/ +/** Flash Erase Enable Register */ +#define MEMCTL_FLASHERASE MMIO32(MEMCTL_BASE + 0x0020) +#define MEMCTL_FLASHERASE_PAGE_ERASE (0x8C799CA7) +#define MEMCTL_FLASHERASE_MASS_PAGE_ERASE (0x09EE76C9) +#define MEMCTL_FLASHERASE_INFO3_ERASE (0x1266FF45) +/**@}*/ + +/**@}*/ + +BEGIN_DECLS + +/** + * @defgroup memctl_api Memory Controller API + * @ingroup peripheral_apis + * @brief PAC5xx MEMCTL Driver + * @author @htmlonly © @endhtmlonly 2020 Kevin Stefanik + * @date March 7, 2020 + * + * This library supports the MEMCTL module in the PAC55xx SoC from Qorvo. + * + * LGPL License Terms @ref lgpl_license + */ + +/*@{*/ + +/** Set the number of wait states for Flash reads. + * @param[in] wstate Wait states: 0-15 + */ +void memctl_flash_set_wstate(uint32_t wstate); +/** Set the MCLK divisor. + * @param[in] div HCLK to MCLK divisor: 1-16 + */ +void memctl_flash_set_mclkdiv(uint32_t div); +/** Set WRITEWORDCOUNT to 0 to reset the Flash write data buffer */ +void memctl_flash_reset_write_buffer(void); +/** Enable Flash Standby Mode */ +void memctl_flash_standby_mode_enable(void); +/** Disable Flash Standby Mode */ +void memctl_flash_standby_mode_disable(void); +/** Enable Flash cache */ +void memctl_flash_cache_enable(void); +/** Disable Flash cache */ +void memctl_flash_cache_disable(void); +/** Select ROSCCLK as input to Flash Memory Controller */ +void memctl_flash_select_roscclk(void); +/** Select MCLK as input to Flash Memory Controller */ +void memctl_flash_select_mclk(void); +/** Enable SRAM ECC */ +void memctl_sram_ecc_enable(void); +/** Disable SRAM ECC */ +void memctl_sram_ecc_disable(void); +/** Enable SRAM ECC Single Bit Detection Interrupt */ +void memctl_sram_ecc_single_bit_interrupt_enable(void); +/** Disable SRAM ECC Single Bit Detection Interrupt */ +void memctl_sram_ecc_single_bit_interrupt_disable(void); +/** Enable SRAM ECC Dual Bit Detection Interrupt */ +void memctl_sram_ecc_dual_bit_interrupt_enable(void); +/** Disable SRAM ECC Dual Bit Detection Interrupt */ +void memctl_sram_ecc_dual_bit_interrupt_disable(void); +/** Enable Invalid Memory Access Interrupt */ +void memctl_invaddr_interrupt_enable(void); +/** Disable Invalid Memory Access Interrupt */ +void memctl_invaddr_interrupt_disable(void); + +/**@}*/ + +END_DECLS + +#endif /* LIBOPENCM3_PAC55XX_MEMCTL_H_ */ diff --git a/lib/pac55xx/Makefile b/lib/pac55xx/Makefile index 463340ba..eee1dfdd 100644 --- a/lib/pac55xx/Makefile +++ b/lib/pac55xx/Makefile @@ -36,7 +36,9 @@ TGT_CFLAGS += $(STANDARD_FLAGS) ARFLAGS = rcs OBJS += can.o +OBJS += ccs.o OBJS += gpio.o +OBJS += memctl.o VPATH += ../cm3 diff --git a/lib/pac55xx/ccs.c b/lib/pac55xx/ccs.c new file mode 100644 index 00000000..0d817bbc --- /dev/null +++ b/lib/pac55xx/ccs.c @@ -0,0 +1,261 @@ +/** + * @brief PAC55xxxx CCS Driver + * @author @htmlonly © @endhtmlonly 2020 Kevin Stefanik + * @date March 7, 2020 + * + * This library supports the CCS module in the PAC55xx SoC from Qorvo. + * + * LGPL License Terms @ref lgpl_license + */ +/* + * This file is part of the libopencm3 project. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#include +#include +#include +#include + +static volatile uint32_t ccs_extclk_frequency = 0; +static volatile uint32_t ccs_frclk_frequency = CCS_ROSC_FREQ; +static volatile uint32_t ccs_sclk_frequency = CCS_ROSC_FREQ; +static volatile uint32_t ccs_pll_clk_frequency = 0; +static volatile uint32_t ccs_hclk_frequency = CCS_ROSC_FREQ; +static volatile uint32_t ccs_aclk_frequency = CCS_ROSC_FREQ; +static volatile uint32_t ccs_pclk_frequency = CCS_ROSC_FREQ; + +void ccs_frclkmux_select(uint32_t sel) { + CCSCTL = (CCSCTL & ~CCS_CTL_FRCLKMUXSEL(CCS_CTL_FRCLKMUXSEL_MASK)) | CCS_CTL_FRCLKMUXSEL(sel); +} +void ccs_rosc_enable(void) { + CCSCTL |= CCS_CTL_ROSCEN; +} +void ccs_rosc_disable(void) { + CCSCTL &= ~CCS_CTL_ROSCEN; +} +void ccs_sclkmux_select_frclk(void) { + CCSCTL &= ~CCS_CTL_SCLKMUXSEL; +} +void ccs_sclkmux_select_pllclk(void) { + CCSCTL |= CCS_CTL_SCLKMUXSEL; +} +void ccs_clkfail_enable(void) { + CCSCTL |= CCS_CTL_CLKFAILEN; +} +void ccs_clkfail_disable(void) { + CCSCTL &= ~CCS_CTL_CLKFAILEN; +} +void ccs_clkfailmux_select_frclk(void) { + CCSCTL &= ~CCS_CTL_CLKFAILMUXSEL; +} +void ccs_clkfailmux_select_pllclk(void) { + CCSCTL |= CCS_CTL_CLKFAILMUXSEL; +} +void ccs_ldo_enable(void) { + CCSCTL |= CCS_CTL_LDOEN; +} +void ccs_ldo_disable(void) { + CCSCTL &= ~CCS_CTL_LDOEN; +} +void ccs_pclk_enable(void) { + CCSCTL |= CCS_CTL_PCLKEN; +} +void ccs_pclk_disable(void) { + CCSCTL &= ~CCS_CTL_PCLKEN; +} +void ccs_aclk_enable(void) { + CCSCTL |= CCS_CTL_ACLKEN; +} +void ccs_aclk_disable(void) { + CCSCTL &= ~CCS_CTL_ACLKEN; +} +void ccs_adcclk_enable(void) { + CCSCTL |= CCS_CTL_ADCCLKEN; +} +void ccs_adcclk_disable(void) { + CCSCTL &= ~CCS_CTL_ADCCLKEN; +} +void ccs_stclk_sleep_enable(void) { + CCSCTL |= CCS_CTL_STCLKSLPEN; +} +void ccs_stclk_sleep_disable(void) { + CCSCTL &= ~CCS_CTL_STCLKSLPEN; +} +void ccs_set_pclkdiv(uint32_t div) { + CCSCTL = (CCSCTL & ~CCS_CTL_PCLKDIV(8)) | CCS_CTL_PCLKDIV(div); +} +void ccs_set_aclkdiv(uint32_t div) { + CCSCTL = (CCSCTL & ~CCS_CTL_ACLKDIV(8)) | CCS_CTL_ACLKDIV(div); +} +void ccs_set_hclkdiv(uint32_t div) { + CCSCTL = (CCSCTL & ~CCS_CTL_HCLKDIV(8)) | CCS_CTL_HCLKDIV(div); +} +void ccs_pll_enable(void) { + CCSPLLCTL |= CCS_PLLCTL_PLLEN; +} +void ccs_pll_disable(void) { + CCSPLLCTL &= ~CCS_PLLCTL_PLLEN; +} +bool ccs_pll_locked(void) { + return (CCSPLLCTL & CCS_PLLCTL_PLLLOCK) == CCS_PLLCTL_PLLLOCK; +} +void ccs_pll_bypass_enable(void) { + CCSPLLCTL |= CCS_PLLCTL_PLLBP; +} +void ccs_pll_bypass_disable(void) { + CCSPLLCTL &= ~CCS_PLLCTL_PLLBP; +} +void ccs_pll_set_outdiv(uint32_t div) { + CCSPLLCTL = (CCSPLLCTL & ~CCS_PLLCTL_PLLOUTDIV(CCS_PLLCTL_PLLOUTDIV_MASK)) | CCS_PLLCTL_PLLOUTDIV(div); +} +void ccs_pll_set_indiv(uint32_t div) { + if (div <= 15 && div >= 1) { + CCSPLLCTL = (CCSPLLCTL & ~CCS_PLLCTL_PLLINDIV(CCS_PLLCTL_PLLINDIV_MASK)) | CCS_PLLCTL_PLLINDIV(div); + } else { + cm3_assert_not_reached(); + } +} +void ccs_pll_set_fbdiv(uint32_t div) { + if (div <= 16383 && div >= 4) { + CCSPLLCTL = (CCSPLLCTL & ~CCS_PLLCTL_PLLFBDIV(CCS_PLLCTL_PLLFBDIV_MASK)) | CCS_PLLCTL_PLLFBDIV(div); + } else { + cm3_assert_not_reached(); + } +} +void css_pll_config_enable(uint32_t indiv, uint32_t fbdiv, uint32_t outdiv) { + ccs_pll_disable(); + ccs_pll_set_fbdiv(fbdiv); + ccs_pll_set_outdiv(outdiv); + ccs_pll_set_indiv(indiv); + ccs_pll_enable(); + while (!ccs_pll_locked()) ; /* Wait for PLL lock ~500us */ +} +uint32_t ccs_get_peripheral_clk_freq(uint32_t periph, uint32_t select) { + switch (periph) { + case ADC_BASE: + return ccs_sclk_frequency; + case I2C_BASE: /* fall through */ + case USARTA_BASE: /* fall through */ + case USARTB_BASE: /* fall through */ + case USARTC_BASE: /* fall through */ + case USARTD_BASE: /* fall through */ + case CAN_BASE: /* fall through */ + case GPTIMERA_BASE: /* fall through */ + case GPTIMERB_BASE: + return ccs_pclk_frequency; + case TIMERA_BASE: /* fall through */ + case TIMERB_BASE: /* fall through */ + case TIMERC_BASE: /* fall through */ + case TIMERD_BASE: + return (select == 0) ? ccs_pclk_frequency : ccs_aclk_frequency; + case MEMCTL_BASE: + return (select == 0) ? CCS_ROSC_FREQ : ccs_hclk_frequency; + case WWDT_BASE: + return (select == 0) ? ccs_frclk_frequency : CCS_ROSC_FREQ; + case RTC_BASE: + return ccs_frclk_frequency; + case CRC_BASE: /* fall through */ + case SYS_TICK_BASE: + return ccs_hclk_frequency; + default: + cm3_assert_not_reached(); + } +} + +void ccs_reset_clocks(void) { + CCSCTL = CCS_CTL_LDOEN | CCS_CTL_ROSCEN | + CCS_CTL_PCLKEN | CCS_CTL_ACLKEN | + CCS_CTL_ADCCLKEN | CCS_CTL_STCLKSLPEN; + CCSPLLCTL = 0; +} + +void ccs_configure_clocks(const struct ccs_clk_config *config) { + MEMCTL_FLASHLOCK = MEMCTL_FLASHLOCK_ALLOW_MEMCTL_WRITE; + + ccs_reset_clocks(); /* set safe defaults */ + ccs_frclkmux_select(CCS_CTL_FRCLKMUXSEL_ROSC); + ccs_sclkmux_select_frclk(); + memctl_flash_select_roscclk(); + + if (config->mem_enable_cache) { + memctl_flash_cache_enable(); + } else { + memctl_flash_cache_disable(); + } + + ccs_frclkmux_select(CCS_CTL_FRCLKMUXSEL_CLKREF); /* switch frclk to 4MHz CLKREF */ + + switch (config->frclk_source) { + case CCS_CTL_FRCLKMUXSEL_ROSC: + ccs_frclkmux_select(CCS_CTL_FRCLKMUXSEL_ROSC); + ccs_frclk_frequency = CCS_ROSC_FREQ; + break; + case CCS_CTL_FRCLKMUXSEL_CLKREF: + ccs_frclkmux_select(CCS_CTL_FRCLKMUXSEL_CLKREF); + ccs_frclk_frequency = CCS_CLKREF_FREQ; + break; + case CCS_CTL_FRCLKMUXSEL_EXTCLK: + if (config->extclk_frequency > CCS_EXTCLK_MAX_FREQ + || config->extclk_frequency == 0) { + cm3_assert_not_reached(); + } + ccs_frclkmux_select(CCS_CTL_FRCLKMUXSEL_EXTCLK); + ccs_frclk_frequency = ccs_extclk_frequency = config->extclk_frequency; + break; + default: + cm3_assert_not_reached(); + } + + if (config->sclk_source == CCS_CTL_SCLKMUXSEL_FRCLK) { + ccs_set_hclkdiv(config->hclkdiv); + ccs_set_aclkdiv(config->aclkdiv); + memctl_flash_set_wstate(config->mem_wstate); + ccs_sclkmux_select_frclk(); + memctl_flash_set_mclkdiv(config->mem_mclkdiv); + if (config->mem_mclksel == false) { + memctl_flash_select_roscclk(); + } else { + memctl_flash_select_mclk(); + } + ccs_sclk_frequency = ccs_frclk_frequency; + } else if (config->sclk_source == CCS_CTL_SCLKMUXSEL_PLLCLK) { + css_pll_config_enable(config->pll_indiv, config->pll_fbdiv, config->pll_outdiv); + ccs_set_hclkdiv(config->hclkdiv); + ccs_set_aclkdiv(config->aclkdiv); + memctl_flash_set_wstate(config->mem_wstate); + ccs_sclkmux_select_pllclk(); + memctl_flash_set_mclkdiv(config->mem_mclkdiv); + if (config->mem_mclksel == false) { + memctl_flash_select_roscclk(); + } else { + memctl_flash_select_mclk(); + } + ccs_pll_clk_frequency = ((ccs_frclk_frequency * config->pll_fbdiv) / config->pll_indiv) >> config->pll_outdiv; + ccs_sclk_frequency = ccs_pll_clk_frequency; + } else { + cm3_assert_not_reached(); + } + ccs_set_pclkdiv(config->pclkdiv); + ccs_pclk_enable(); + ccs_aclk_enable(); + ccs_adcclk_enable(); + ccs_stclk_sleep_disable(); + + ccs_hclk_frequency = ccs_sclk_frequency / config->hclkdiv; + ccs_aclk_frequency = ccs_sclk_frequency / config->aclkdiv; + ccs_pclk_frequency = ccs_hclk_frequency / config->pclkdiv; + + MEMCTL_FLASHLOCK = MEMCTL_FLASHLOCK_CLEAR; +} diff --git a/lib/pac55xx/memctl.c b/lib/pac55xx/memctl.c new file mode 100644 index 00000000..86a66f29 --- /dev/null +++ b/lib/pac55xx/memctl.c @@ -0,0 +1,78 @@ +/** + * @brief PAC55xxxx Memory Controller Driver + * @author @htmlonly © @endhtmlonly 2020 Kevin Stefanik + * @date April 1, 2020 + * + * This library supports the Memory Controller in the PAC55xx SoC from Qorvo. + * + * LGPL License Terms @ref lgpl_license + */ +/* + * This file is part of the libopencm3 project. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#include + +void memctl_flash_set_wstate(uint32_t wstate) { + MEMCTL_MEMCTLR = (MEMCTL_MEMCTLR & ~MEMCTL_MEMCTLR_WSTATE(MEMCTL_MEMCTLR_WSTATE_MASK)) | MEMCTL_MEMCTLR_WSTATE(wstate); +} +void memctl_flash_set_mclkdiv(uint32_t div) { + MEMCTL_MEMCTLR = (MEMCTL_MEMCTLR & ~MEMCTL_MEMCTLR_MCLKDIV(16)) | MEMCTL_MEMCTLR_MCLKDIV(div); +} +void memctl_flash_reset_write_buffer(void) { + MEMCTL_MEMCTLR = (MEMCTL_MEMCTLR & ~MEMCTL_MEMCTLR_WRITEWORDCNT(MEMCTL_MEMCTLR_WRITEWORDCNT_MASK)); +} +void memctl_flash_standby_mode_enable(void) { + MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_STBY; +} +void memctl_flash_standby_mode_disable(void) { + MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_STBY; +} +void memctl_flash_cache_enable(void) { + MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_CACHEDIS; +} +void memctl_flash_cache_disable(void) { + MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_CACHEDIS; +} +void memctl_flash_select_roscclk(void) { + MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_MCLKSEL; +} +void memctl_flash_select_mclk(void) { + MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_MCLKSEL; +} +void memctl_sram_ecc_enable(void) { + MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_ECCDIS; +} +void memctl_sram_ecc_disable(void) { + MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_ECCDIS; +} +void memctl_sram_ecc_single_bit_interrupt_enable(void) { + MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_SEIE; +} +void memctl_sram_ecc_single_bit_interrupt_disable(void) { + MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_SEIE; +} +void memctl_sram_ecc_dual_bit_interrupt_enable(void) { + MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_DEIE; +} +void memctl_sram_ecc_dual_bit_interrupt_disable(void) { + MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_DEIE; +} +void memctl_invaddr_interrupt_enable(void) { + MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_INVADDRIE; +} +void memctl_invaddr_interrupt_disable(void) { + MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_INVADDRIE; +}