From 031b83cff34cd0be9e0515b6e0e5d2cd698dd0e3 Mon Sep 17 00:00:00 2001
From: Daniel Pereira Volpato <dpo@certi.org.br>
Date: Tue, 17 Sep 2019 11:29:49 -0600
Subject: [PATCH] arch/arm/src/stm32f0l0g0/stm32*_pwr.c and stm32g0_rcc.c: 
 Scale dynamic voltage and flash wait states properly on STM32G0 chips.

arch/arm/src/stm32f0l0g0/stm32g0_rcc.c: Set VOS and flash wait states properly
arch/arm/src/stm32f0l0g0/stm32f0l0_pwr.c: Renamed from arch/arm/src/stm32f0l0g0/stm32_pwr.c
arch/arm/src/stm32f0l0g0/stm32g0_pwr.c: Preliminary implementation of PWR module for STM32G0 (stm32_pwr_setvos() only)
---
 arch/arm/src/stm32f0l0g0/stm32_pwr.c   | 385 ++-----------------------
 arch/arm/src/stm32f0l0g0/stm32_pwr.h   |   5 +-
 arch/arm/src/stm32f0l0g0/stm32g0_rcc.c |  58 +++-
 3 files changed, 74 insertions(+), 374 deletions(-)

diff --git a/arch/arm/src/stm32f0l0g0/stm32_pwr.c b/arch/arm/src/stm32f0l0g0/stm32_pwr.c
index 18907f98e5..f45aad7237 100644
--- a/arch/arm/src/stm32f0l0g0/stm32_pwr.c
+++ b/arch/arm/src/stm32f0l0g0/stm32_pwr.c
@@ -1,8 +1,8 @@
-/************************************************************************************
- * arch/arm/src/stm32/stm32_pwr.c
+/****************************************************************************
+ * arch/arm/src/stm32f0l0g0/stm32_pwr.c
  *
- *   Copyright (C) 2018 Gregory Nutt. All rights reserved.
- *   Authors: Gregory Nutt <gnutt@nuttx.org>
+ *   Copyright (C) 2019 Gregory Nutt. All rights reserved.
+ *   Author: Daniel Pereira Volpato <dpo@certi.org.br>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -31,376 +31,25 @@
  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  *
- ************************************************************************************/
+ ****************************************************************************/
 
-/************************************************************************************
+/****************************************************************************
  * Included Files
- ************************************************************************************/
+ ****************************************************************************/
 
 #include <nuttx/config.h>
+#include "chip.h"
 
-#include <stdint.h>
-#include <stdbool.h>
-#include <errno.h>
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
 
-#include <nuttx/arch.h>
-#include <nuttx/irq.h>
-
-#include "up_arch.h"
-#include "stm32_pwr.h"
-
-#if defined(CONFIG_STM32F0L0G0_PWR)
-
-/************************************************************************************
- * Private Data
- ************************************************************************************/
-
-/* Parts only support a single Wake-up pin do not include the numeric suffix
- * in the naming.
+/* This file is only a thin shell that includes the proper PWR implementation
+ * according to the selected MCU family.
  */
 
-#ifndef PWR_CSR_EWUP1
-#  define PWR_CSR_EWUP1 PWR_CSR_EWUP
+#if defined(CONFIG_STM32F0L0G0_STM32G0)
+#  include "stm32g0_pwr.c"
+#else
+#  include "stm32f0l0_pwr.c"
 #endif
-
-/************************************************************************************
- * Private Data
- ************************************************************************************/
-
-static uint16_t g_bkp_writable_counter = 0;
-
-/************************************************************************************
- * Private Functions
- ************************************************************************************/
-
-static inline uint32_t stm32_pwr_getreg32(uint8_t offset)
-{
-  return getreg32(STM32_PWR_BASE + (uint32_t)offset);
-}
-
-static inline void stm32_pwr_putreg32(uint8_t offset, uint32_t value)
-{
-  putreg32(value, STM32_PWR_BASE + (uint32_t)offset);
-}
-
-static inline void stm32_pwr_modifyreg32(uint8_t offset, uint32_t clearbits,
-                                         uint32_t setbits)
-{
-  modifyreg32(STM32_PWR_BASE + (uint32_t)offset, clearbits, setbits);
-}
-
-/************************************************************************************
- * Public Functions
- ************************************************************************************/
-
-/************************************************************************************
- * Name: stm32_pwr_initbkp
- *
- * Description:
- *   Insures the referenced count access to the backup domain (RTC registers,
- *   RTC backup data registers and backup SRAM is consistent with the HW state
- *   without relying on a variable.
- *
- *   NOTE: This function should only be called by SoC Start up code.
- *
- * Input Parameters:
- *   writable - True: enable ability to write to backup domain registers
- *
- * Returned Value:
- *   None
- *
- ************************************************************************************/
-
-void stm32_pwr_initbkp(bool writable)
-{
-  uint16_t regval;
-
-  /* Make the HW not writable */
-
-  regval = stm32_pwr_getreg32(STM32_PWR_CR_OFFSET);
-  regval &= ~PWR_CR_DBP;
-  stm32_pwr_putreg32(STM32_PWR_CR_OFFSET, regval);
-
-  /* Make the reference count agree */
-
-  g_bkp_writable_counter = 0;
-  stm32_pwr_enablebkp(writable);
-}
-
-/************************************************************************************
- * Name: stm32_pwr_enablebkp
- *
- * Description:
- *   Enables access to the backup domain (RTC registers, RTC backup data registers
- *   and backup SRAM).
- *
- *   NOTE: Reference counting is used in order to supported nested calls to this
- *   function.  As a consequence, every call to stm32_pwr_enablebkp(true) must
- *   be followed by a matching call to stm32_pwr_enablebkp(false).
- *
- * Input Parameters:
- *   writable - True: enable ability to write to backup domain registers
- *
- * Returned Value:
- *   None
- *
- ************************************************************************************/
-
-void stm32_pwr_enablebkp(bool writable)
-{
-  irqstate_t flags;
-  uint16_t regval;
-  bool waswritable;
-  bool wait = false;
-
-  flags = enter_critical_section();
-
-  /* Get the current state of the STM32 PWR control register */
-
-  regval      = stm32_pwr_getreg32(STM32_PWR_CR_OFFSET);
-  waswritable = ((regval & PWR_CR_DBP) != 0);
-
-  if (writable)
-    {
-      DEBUGASSERT(g_bkp_writable_counter < UINT16_MAX);
-      g_bkp_writable_counter++;
-    }
-  else if (g_bkp_writable_counter > 0)
-    {
-      g_bkp_writable_counter--;
-    }
-
-  /* Enable or disable the ability to write */
-
-  if (waswritable && g_bkp_writable_counter == 0)
-    {
-      /* Disable backup domain access */
-
-      regval &= ~PWR_CR_DBP;
-      stm32_pwr_putreg32(STM32_PWR_CR_OFFSET, regval);
-    }
-  else if (!waswritable && g_bkp_writable_counter > 0)
-    {
-      /* Enable backup domain access */
-
-      regval |= PWR_CR_DBP;
-      stm32_pwr_putreg32(STM32_PWR_CR_OFFSET, regval);
-
-      wait = true;
-    }
-
-  leave_critical_section(flags);
-
-  if (wait)
-    {
-      /* Enable does not happen right away */
-
-      up_udelay(4);
-    }
-}
-
-/************************************************************************************
- * Name: stm32_pwr_enablewkup
- *
- * Description:
- *   Enables the WKUP pin.
- *
- * Input Parameters:
- *   wupin - Selects the WKUP pin to enable/disable
- *   wupon - state to set it to
- *
- * Returned Value:
- *   Zero (OK) is returned on success; A negated errno value is returned on any
- *   failure.  The only cause of failure is if the selected MCU does not support
- *   the requested wakeup pin.
- *
- ************************************************************************************/
-
-int stm32_pwr_enablewkup(enum stm32_pwr_wupin_e wupin, bool wupon)
-{
-  uint16_t pinmask;
-
-  /* Select the PWR_CSR bit associated with the requested wakeup pin */
-
-  switch (wupin)
-    {
-      case PWR_WUPIN_1:    /* Wake-up pin 1 (all parts) */
-        pinmask = PWR_CSR_EWUP1;
-        break;
-
-#ifdef HAVE_PWR_WKUP2
-      case PWR_WUPIN_2:    /* Wake-up pin 2 */
-        pinmask = PWR_CSR_EWUP2;
-        break;
-#endif
-
-#ifdef HAVE_PWR_WKUP3
-      case PWR_WUPIN_3:     /* Wake-up pin 3 */
-        pinmask = PWR_CSR_EWUP3;
-        break;
-#endif
-
-      default:
-        return -EINVAL;
-    }
-
-  /* Set/clear the the wakeup pin enable bit in the CSR.  This must be done
-   * within a critical section because the CSR is shared with other functions
-   * that may be running concurrently on another thread.
-   */
-
-  if (wupon)
-    {
-      /* Enable the wakeup pin by setting the bit in the CSR. */
-
-      stm32_pwr_modifyreg32(STM32_PWR_CSR_OFFSET, 0, pinmask);
-    }
-  else
-    {
-      /* Disable the wakeup pin by clearing the bit in the CSR. */
-
-      stm32_pwr_modifyreg32(STM32_PWR_CSR_OFFSET, pinmask, 0);
-    }
-
-  return OK;
-}
-
-/************************************************************************************
- * Name: stm32_pwr_getsbf
- *
- * Description:
- *   Return the standby flag.
- *
- ************************************************************************************/
-
-bool stm32_pwr_getsbf(void)
-{
-  return (stm32_pwr_getreg32(STM32_PWR_CSR_OFFSET) & PWR_CSR_SBF) != 0;
-}
-
-/************************************************************************************
- * Name: stm32_pwr_getwuf
- *
- * Description:
- *   Return the wakeup flag.
- *
- ************************************************************************************/
-
-bool stm32_pwr_getwuf(void)
-{
-  return (stm32_pwr_getreg32(STM32_PWR_CSR_OFFSET) & PWR_CSR_WUF) != 0;
-}
-
-/************************************************************************************
- * Name: stm32_pwr_setvos
- *
- * Description:
- *   Set voltage scaling for EnergyLite devices.
- *
- * Input Parameters:
- *   vos - Properly aligned voltage scaling select bits for the PWR_CR register.
- *
- * Returned Value:
- *   None
- *
- * Assumptions:
- *   At present, this function is called only from initialization logic.  If used
- *   for any other purpose that protection to assure that its operation is atomic
- *   will be required.
- *
- ************************************************************************************/
-
-#ifdef CONFIG_STM32F0L0G0_ENERGYLITE
-void stm32_pwr_setvos(uint16_t vos)
-{
-  uint16_t regval;
-
-  /* The following sequence is required to program the voltage regulator ranges:
-   * 1. Check VDD to identify which ranges are allowed...
-   * 2. Poll VOSF bit of in PWR_CSR. Wait until it is reset to 0.
-   * 3. Configure the voltage scaling range by setting the VOS bits in the PWR_CR
-   *    register.
-   * 4. Poll VOSF bit of in PWR_CSR register. Wait until it is reset to 0.
-   */
-
-  while ((stm32_pwr_getreg32(STM32_PWR_CSR_OFFSET) & PWR_CSR_VOSF) != 0)
-    {
-    }
-
-  regval  = stm32_pwr_getreg32(STM32_PWR_CR_OFFSET);
-  regval &= ~PWR_CR_VOS_MASK;
-  regval |= (vos & PWR_CR_VOS_MASK);
-  stm32_pwr_putreg32(STM32_PWR_CR_OFFSET, regval);
-
-  while ((stm32_pwr_getreg32(STM32_PWR_CSR_OFFSET) & PWR_CSR_VOSF) != 0)
-    {
-    }
-}
-
-/************************************************************************************
- * Name: stm32_pwr_setpvd
- *
- * Description:
- *   Sets power voltage detector
- *
- * Input Parameters:
- *   pls - PVD level
- *
- * Returned Value:
- *   None
- *
- * Assumptions:
- *   At present, this function is called only from initialization logic.  If used
- *   for any other purpose that protection to assure that its operation is atomic
- *   will be required.
- *
- ************************************************************************************/
-
-void stm32_pwr_setpvd(uint16_t pls)
-{
-  uint16_t regval;
-
-  /* Set PLS */
-
-  regval  = stm32_pwr_getreg32(STM32_PWR_CR_OFFSET);
-  regval &= ~PWR_CR_PLS_MASK;
-  regval |= (pls & PWR_CR_PLS_MASK);
-
-  /* Write value to register */
-
-  stm32_pwr_putreg32(STM32_PWR_CR_OFFSET, regval);
-}
-
-/************************************************************************************
- * Name: stm32_pwr_enablepvd
- *
- * Description:
- *   Enable the Programmable Voltage Detector
- *
- ************************************************************************************/
-
-void stm32_pwr_enablepvd(void)
-{
-  /* Enable PVD by setting the PVDE bit in PWR_CR register. */
-
-  stm32_pwr_modifyreg32(STM32_PWR_CR_OFFSET, 0, PWR_CR_PVDE);
-}
-
-/************************************************************************************
- * Name: stm32_pwr_disablepvd
- *
- * Description:
- *   Disable the Programmable Voltage Detector
- *
- ************************************************************************************/
-
-void stm32_pwr_disablepvd(void)
-{
-  /* Disable PVD by clearing the PVDE bit in PWR_CR register. */
-
-  stm32_pwr_modifyreg32(STM32_PWR_CR_OFFSET, PWR_CR_PVDE, 0);
-}
-
-#endif /* CONFIG_STM32F0L0G0_ENERGYLITE */
-
-#endif /* CONFIG_STM32F0L0G0_PWR */
diff --git a/arch/arm/src/stm32f0l0g0/stm32_pwr.h b/arch/arm/src/stm32f0l0g0/stm32_pwr.h
index 563ba4b37a..55014618f2 100644
--- a/arch/arm/src/stm32f0l0g0/stm32_pwr.h
+++ b/arch/arm/src/stm32f0l0g0/stm32_pwr.h
@@ -3,6 +3,7 @@
  *
  *   Copyright (C) 2018 Gregory Nutt. All rights reserved.
  *   Authors: Gregory Nutt <gnutt@nuttx.org>
+ *            Daniel Pereira Volpato <dpo@certi.org.br>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -181,8 +182,9 @@ bool stm32_pwr_getwuf(void);
  *
  ************************************************************************************/
 
-#ifdef CONFIG_STM32F0L0G0_ENERGYLITE
+#if defined(CONFIG_STM32F0L0G0_ENERGYLITE) || defined(CONFIG_STM32F0L0G0_STM32G0)
 void stm32_pwr_setvos(uint16_t vos);
+#endif /* CONFIG_STM32F0L0G0_ENERGYLITE || CONFIG_STM32F0L0G0_STM32G0 */
 
 /************************************************************************************
  * Name: stm32_pwr_setpvd
@@ -201,6 +203,7 @@ void stm32_pwr_setvos(uint16_t vos);
  *
  ************************************************************************************/
 
+#if defined(CONFIG_STM32F0L0G0_ENERGYLITE)
 void stm32_pwr_setpvd(uint16_t pls);
 
 /************************************************************************************
diff --git a/arch/arm/src/stm32f0l0g0/stm32g0_rcc.c b/arch/arm/src/stm32f0l0g0/stm32g0_rcc.c
index 83cc1a4112..6308ffa9a6 100644
--- a/arch/arm/src/stm32f0l0g0/stm32g0_rcc.c
+++ b/arch/arm/src/stm32f0l0g0/stm32g0_rcc.c
@@ -448,7 +448,7 @@ static void stm32_stdclockconfig(void)
   uint16_t pwrcr;
 #endif
   uint32_t pwr_vos;
-  bool flash_1ws;
+  uint32_t flash_ws;
 
   /* Enable PWR clock from APB1 to give access to PWR_CR register */
 
@@ -456,8 +456,52 @@ static void stm32_stdclockconfig(void)
   regval |= RCC_APB1ENR_PWREN;
   putreg32(regval, STM32_RCC_APB1ENR);
 
-#warning TODO: configure VOS range
-  UNUSED(pwr_vos);
+  /* Two voltage ranges are available:
+   *
+   * Range 1: High-performance range (default)
+   *          Typical output voltage 1.2 V
+   *          SYSLCK up to 64 MHz
+   *
+   * Range 2: Low-power range
+   *          Typical output voltage 1.0V
+   *          SYSLCK up to 16 MHz
+   *
+   * Flash wait states (latency) according to range and HCLK:
+   *
+   * Range 1:
+   * - Flash 0WS if HCLK <= 24
+   * - Flash 1WS if HCLK <= 48
+   * - Flash 2WS if HCLK <= 64
+   *
+   * Range 2:
+   * - Flash 0WS if HCLK <= 8
+   * - Flash 1WS if HCLK <= 16
+   *
+   * Where HCLK = (SYSCLK / HPRE div)
+   */
+
+  if (STM32_SYSCLK_FREQUENCY > 16000000)
+    {
+      pwr_vos = PWR_CR1_VOS_RANGE1;
+
+      if (STM32_HCLK_FREQUENCY <= 24000000)
+        flash_ws = FLASH_ACR_LATENCY_0;
+      else if (STM32_HCLK_FREQUENCY <= 48000000)
+        flash_ws = FLASH_ACR_LATENCY_1;
+      else
+        flash_ws = FLASH_ACR_LATENCY_2;
+    }
+  else
+    {
+      pwr_vos = PWR_CR1_VOS_RANGE2;
+
+      if (STM32_HCLK_FREQUENCY <= 8000000)
+        flash_ws = FLASH_ACR_LATENCY_0;
+      else
+        flash_ws = FLASH_ACR_LATENCY_1;
+    }
+
+  stm32_pwr_setvos(pwr_vos);
 
 #if defined(CONFIG_STM32F0L0G0_RTC_HSECLOCK) || defined(CONFIG_LCD_HSECLOCK)
   /* If RTC / LCD selects HSE as clock source, the RTC prescaler
@@ -533,8 +577,12 @@ static void stm32_stdclockconfig(void)
 
 #endif
 
-#warning TODO: configure flash latency
-  UNUSED(flash_1ws);
+  /* Configure FLASH wait states and enable prefetch */
+
+  regval  = getreg32(STM32_FLASH_ACR);
+  regval &= ~FLASH_ACR_LATENCY_MASK;
+  regval |= (flash_ws & FLASH_ACR_LATENCY_MASK) | FLASH_ACR_PRFTEN;
+  putreg32(regval, STM32_FLASH_ACR);
 
   /* Set the HCLK source/divider */