hw/mcu/dialog: Apply trim and preferred values

So far we assumed that 1st stage bootloader applied all trim values
stored in CS and they are valid. However, since power domains can be
disabled we need to make sure proper settings are reapplied once they
are enabled.

This patch automates this process:
- read trim values for each applicable PD on init
- apply trim values once PD is enabled (on 1st acquire)
- apply preferred values on top of this

This means that after waking up from extended sleep (where most of power
domains are disabled) all settings are reapplied correctly.
diff --git a/hw/mcu/dialog/da1469x/include/mcu/da1469x_pd.h b/hw/mcu/dialog/da1469x/include/mcu/da1469x_pd.h
index acc552b..5a9e3bd 100644
--- a/hw/mcu/dialog/da1469x/include/mcu/da1469x_pd.h
+++ b/hw/mcu/dialog/da1469x/include/mcu/da1469x_pd.h
@@ -29,14 +29,17 @@
 #endif
 
 /* Available (controllable) power domains */
-#define MCU_PD_DOMAIN_PER           0
-#define MCU_PD_DOMAIN_RAD           1
-#define MCU_PD_DOMAIN_TIM           2
-#define MCU_PD_DOMAIN_COM           3
+#define MCU_PD_DOMAIN_SYS           0
+#define MCU_PD_DOMAIN_PER           1
+#define MCU_PD_DOMAIN_RAD           2
+#define MCU_PD_DOMAIN_TIM           3
+#define MCU_PD_DOMAIN_COM           4
 
-#define MCU_PD_DOMAIN_COUNT         4
+#define MCU_PD_DOMAIN_COUNT         5
 
+int da1469x_pd_init(void);
 int da1469x_pd_acquire(uint8_t pd);
+int da1469x_pd_acquire_noconf(uint8_t pd);
 int da1469x_pd_release(uint8_t pd);
 int da1469x_pd_release_nowait(uint8_t pd);
 
diff --git a/hw/mcu/dialog/da1469x/src/da1469x_pd.c b/hw/mcu/dialog/da1469x/src/da1469x_pd.c
index 1f95f90..20ac45c 100644
--- a/hw/mcu/dialog/da1469x/src/da1469x_pd.c
+++ b/hw/mcu/dialog/da1469x/src/da1469x_pd.c
@@ -18,9 +18,11 @@
  */
 
 #include <assert.h>
+#include <stdlib.h>
 #include "defs/error.h"
 #include "mcu/da1469x_hal.h"
 #include "mcu/da1469x_pd.h"
+#include "mcu/da1469x_trimv.h"
 #include "os/util.h"
 
 struct da1469x_pd_desc {
@@ -28,7 +30,14 @@
     uint8_t stat_down_bit; /* up is +1 */
 };
 
+struct da1469x_pd_trimv {
+    uint8_t count;
+    uint32_t *words;
+};
+
 static const struct da1469x_pd_desc g_da1469x_pd_desc[] = {
+    [MCU_PD_DOMAIN_SYS] = { CRG_TOP_PMU_CTRL_REG_SYS_SLEEP_Pos,
+                            CRG_TOP_SYS_STAT_REG_SYS_IS_DOWN_Pos },
     [MCU_PD_DOMAIN_PER] = { CRG_TOP_PMU_CTRL_REG_PERIPH_SLEEP_Pos,
                             CRG_TOP_SYS_STAT_REG_PER_IS_DOWN_Pos },
     [MCU_PD_DOMAIN_RAD] = { CRG_TOP_PMU_CTRL_REG_RADIO_SLEEP_Pos,
@@ -40,9 +49,129 @@
 };
 
 static uint8_t g_da1469x_pd_refcnt[ ARRAY_SIZE(g_da1469x_pd_desc) ];
+static struct da1469x_pd_trimv g_da1469x_pd_trimv[ ARRAY_SIZE(g_da1469x_pd_desc) ];
+
+static void
+da1469x_pd_load_trimv(uint8_t pd, uint8_t group)
+{
+    struct da1469x_pd_trimv *tv;
+
+    assert(pd < ARRAY_SIZE(g_da1469x_pd_desc));
+
+    tv = &g_da1469x_pd_trimv[pd];
+    tv->count = da1469x_trimv_group_num_words_get(group);
+    if (tv->count == 0) {
+        return;
+    }
+
+    tv->words = calloc(tv->count, 4);
+    if (!tv->words) {
+        tv->count = 0;
+        assert(0);
+        return;
+    }
+
+    tv->count = da1469x_trimv_group_read(group, tv->words, tv->count);
+    tv->count /= 2;
+}
+
+static void
+da1469x_pd_apply_trimv(uint8_t pd)
+{
+    struct da1469x_pd_trimv *tv;
+    volatile uint32_t *reg;
+    uint32_t val;
+    int idx;
+
+    assert(pd < ARRAY_SIZE(g_da1469x_pd_desc));
+
+    tv = &g_da1469x_pd_trimv[pd];
+    if (tv->count == 0) {
+        return;
+    }
+
+    for (idx = 0; idx < tv->count; idx++) {
+        reg = (void *)tv->words[idx * 2];
+        val = tv->words[idx * 2 + 1];
+        *reg = val;
+    }
+}
+
+static inline uint32_t
+get_reg32(uint32_t addr)
+{
+    volatile uint32_t *reg = (volatile uint32_t *)addr;
+
+    return *reg;
+}
+
+static inline void
+set_reg32(uint32_t addr, uint32_t val)
+{
+    volatile uint32_t *reg = (volatile uint32_t *)addr;
+
+    *reg = val;
+}
+
+static inline void
+set_reg32_mask(uint32_t addr, uint32_t mask, uint32_t val)
+{
+    volatile uint32_t *reg = (volatile uint32_t *)addr;
+
+    *reg = (*reg & (~mask)) | (val & mask);
+}
+
+static void
+da1469x_pd_apply_preferred(uint8_t pd)
+{
+    switch (pd) {
+    case MCU_PD_DOMAIN_SYS:
+        set_reg32_mask(0x50040400, 0x00000c00, 0x003f6a78);
+        set_reg32_mask(0x50040454, 0x000003ff, 0x00000002);
+        break;
+    case MCU_PD_DOMAIN_TIM:
+        set_reg32_mask(0x50010000, 0x3ff00000, 0x000afd70);
+        set_reg32_mask(0x50010010, 0x000000c0, 0x00000562);
+        set_reg32_mask(0x50010030, 0x43c38002, 0x4801e6b6);
+        set_reg32_mask(0x50010034, 0x007fff00, 0x7500a1a4);
+        set_reg32_mask(0x50010038, 0x00000fff, 0x001e45c4);
+        set_reg32_mask(0x5001003c, 0x40000000, 0x40096255);
+        set_reg32_mask(0x50010040, 0x00c00000, 0x00c00000);
+        set_reg32_mask(0x50010018, 0x000000ff, 0x00000180);
+        break;
+    }
+}
+
+static void
+apply_preferred_pd_aon(void)
+{
+    if (get_reg32(0x500000f8) == 0x00008800) {
+        set_reg32(0x500000f8, 0x00007700);
+    }
+    set_reg32_mask(0x50000050, 0x00001000, 0x00001020);
+    set_reg32(0x500000a4, 0x000000ca);
+    set_reg32_mask(0x50000064, 0x0003ffff, 0x041e6ef4);
+}
 
 int
-da1469x_pd_acquire(uint8_t pd)
+da1469x_pd_init(void)
+{
+    /*
+     * Apply now for always-on domain which, as name suggests, is always on so
+     * need to do this only once.
+     */
+    apply_preferred_pd_aon();
+
+    da1469x_pd_load_trimv(MCU_PD_DOMAIN_SYS, 1);
+    da1469x_pd_load_trimv(MCU_PD_DOMAIN_COM, 2);
+    da1469x_pd_load_trimv(MCU_PD_DOMAIN_TIM, 4);
+    da1469x_pd_load_trimv(MCU_PD_DOMAIN_PER, 5);
+
+    return 0;
+}
+
+static int
+da1469x_pd_acquire_internal(uint8_t pd, bool load)
 {
     uint8_t *refcnt;
     uint32_t primask;
@@ -62,6 +191,11 @@
         bitmask = 1 << (g_da1469x_pd_desc[pd].stat_down_bit + 1);
         while ((CRG_TOP->SYS_STAT_REG & bitmask) == 0);
 
+        if (load) {
+            da1469x_pd_apply_trimv(pd);
+            da1469x_pd_apply_preferred(pd);
+        }
+
         ret = 1;
     }
 
@@ -71,6 +205,18 @@
 }
 
 int
+da1469x_pd_acquire(uint8_t pd)
+{
+    return da1469x_pd_acquire_internal(pd, true);
+}
+
+int
+da1469x_pd_acquire_noconf(uint8_t pd)
+{
+    return da1469x_pd_acquire_internal(pd, false);
+}
+
+int
 da1469x_pd_release(uint8_t pd)
 {
     uint8_t *refcnt;
diff --git a/hw/mcu/dialog/da1469x/src/da1469x_sleep.c b/hw/mcu/dialog/da1469x/src/da1469x_sleep.c
index e2662f5..a476e83 100644
--- a/hw/mcu/dialog/da1469x/src/da1469x_sleep.c
+++ b/hw/mcu/dialog/da1469x/src/da1469x_sleep.c
@@ -19,6 +19,7 @@
 
 #include <assert.h>
 #include "mcu/da1469x_clock.h"
+#include "mcu/da1469x_pd.h"
 #include "mcu/da1469x_pdc.h"
 #include "mcu/da1469x_prail.h"
 #include "mcu/mcu.h"
@@ -65,11 +66,15 @@
         return;
     }
 
+    /* PD_SYS will not be disabled here until we enter deep sleep, so don't wait */
+    da1469x_pd_release_nowait(MCU_PD_DOMAIN_SYS);
+
     mcu_gpio_enter_sleep();
     ret = da1469x_m33_sleep();
     mcu_gpio_exit_sleep();
     if (!ret) {
-        /* We were not sleeping, just return */
+        /* We were not sleeping, no need to apply PD_SYS settings again */
+        da1469x_pd_acquire_noconf(MCU_PD_DOMAIN_SYS);
         return;
     }
 
@@ -77,6 +82,8 @@
     da1469x_prail_dcdc_restore();
 #endif
 
+    da1469x_pd_acquire(MCU_PD_DOMAIN_SYS);
+
     /*
      * If PDC entry for "combo" wakeup is pending, but none of CMAC2SYS, WKUP
      * or VBUS is pending it means we were woken up from JTAG. We need to block
diff --git a/hw/mcu/dialog/da1469x/src/hal_system.c b/hw/mcu/dialog/da1469x/src/hal_system.c
index 53baf10..57a2ca2 100644
--- a/hw/mcu/dialog/da1469x/src/hal_system.c
+++ b/hw/mcu/dialog/da1469x/src/hal_system.c
@@ -23,7 +23,6 @@
 #include "mcu/da1469x_lpclk.h"
 #include "mcu/da1469x_pd.h"
 #include "mcu/da1469x_pdc.h"
-#include "mcu/da1469x_trimv.h"
 #include "hal/hal_system.h"
 #include "os/os_cputime.h"
 
@@ -59,8 +58,6 @@
         g_hal_reset_reason = 0;
     }
 #endif
-
-    da1469x_trimv_init_from_otp();
 }
 
 void
diff --git a/hw/mcu/dialog/da1469x/src/system_da1469x.c b/hw/mcu/dialog/da1469x/src/system_da1469x.c
index 938df82..1dfae27 100644
--- a/hw/mcu/dialog/da1469x/src/system_da1469x.c
+++ b/hw/mcu/dialog/da1469x/src/system_da1469x.c
@@ -22,6 +22,7 @@
 #include "mcu/da1469x_pdc.h"
 #include "mcu/da1469x_prail.h"
 #include "mcu/da1469x_clock.h"
+#include "mcu/da1469x_trimv.h"
 #include <mcu/da1469x_otp.h>
 #include "mcu/mcu.h"
 #include "da1469x_priv.h"
@@ -88,6 +89,18 @@
     NVIC_DisableIRQ(PDC_IRQn);
     NVIC_ClearPendingIRQ(PDC_IRQn);
 
+    /*
+     * We need to parse trim values from CS to apply them when each power domain
+     * is enabled so need to do this before any PD is acquired. Need to initialize
+     * OTP before that.
+     */
+    da1469x_otp_init();
+    da1469x_trimv_init_from_otp();
+    da1469x_pd_init();
+
+    /* PD_SYS is already running so let's acquire it to apply preferred settings */
+    da1469x_pd_acquire(MCU_PD_DOMAIN_SYS);
+
 #if MYNEWT_VAL(OS_SCHEDULING) && MYNEWT_VAL(MCU_DEEP_SLEEP)
     /* Make sure PD_TIM domain is always up for Timer2 to be running */
     da1469x_pd_acquire(MCU_PD_DOMAIN_TIM);
@@ -107,15 +120,10 @@
     da1469x_pdc_ack(idx);
     g_mcu_pdc_combo_idx = idx;
 
-    CRG_TOP->PMU_CTRL_REG |= CRG_TOP_PMU_CTRL_REG_SYS_SLEEP_Msk;
-
     /* Enable cache retainability */
     CRG_TOP->PMU_CTRL_REG |= CRG_TOP_PMU_CTRL_REG_RETAIN_CACHE_Msk;
 #endif
 
-    /* initialize OTP and place in deep standby */
-    da1469x_otp_init();
-
     /* Initialize and configure power rails */
     da1469x_prail_initialize();
 #if MYNEWT_VAL(MCU_DCDC_ENABLE)