blob: 20ac45c914bf4bb26e18e122e9c1397cc3cc432e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#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 {
uint8_t pmu_sleep_bit;
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,
CRG_TOP_SYS_STAT_REG_RAD_IS_DOWN_Pos},
[MCU_PD_DOMAIN_TIM] = { CRG_TOP_PMU_CTRL_REG_TIM_SLEEP_Pos,
CRG_TOP_SYS_STAT_REG_TIM_IS_DOWN_Pos },
[MCU_PD_DOMAIN_COM] = { CRG_TOP_PMU_CTRL_REG_COM_SLEEP_Pos,
CRG_TOP_SYS_STAT_REG_COM_IS_DOWN_Pos},
};
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_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;
uint32_t bitmask;
int ret = 0;
assert(pd < ARRAY_SIZE(g_da1469x_pd_desc));
refcnt = &g_da1469x_pd_refcnt[pd];
__HAL_DISABLE_INTERRUPTS(primask);
assert(*refcnt < UINT8_MAX);
if ((*refcnt)++ == 0) {
bitmask = 1 << g_da1469x_pd_desc[pd].pmu_sleep_bit;
CRG_TOP->PMU_CTRL_REG &= ~bitmask;
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;
}
__HAL_ENABLE_INTERRUPTS(primask);
return ret;
}
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;
uint32_t primask;
uint32_t bitmask;
int ret = 0;
assert(pd < MCU_PD_DOMAIN_COUNT);
refcnt = &g_da1469x_pd_refcnt[pd];
__HAL_DISABLE_INTERRUPTS(primask);
assert(*refcnt > 0);
if (--(*refcnt) == 0) {
bitmask = 1 << g_da1469x_pd_desc[pd].pmu_sleep_bit;
CRG_TOP->PMU_CTRL_REG |= bitmask;
bitmask = 1 << g_da1469x_pd_desc[pd].stat_down_bit;
while ((CRG_TOP->SYS_STAT_REG & bitmask) == 0);
ret = 1;
}
__HAL_ENABLE_INTERRUPTS(primask);
return ret;
}
int
da1469x_pd_release_nowait(uint8_t pd)
{
uint8_t *refcnt;
uint32_t primask;
uint32_t bitmask;
int ret = 0;
assert(pd < MCU_PD_DOMAIN_COUNT);
refcnt = &g_da1469x_pd_refcnt[pd];
__HAL_DISABLE_INTERRUPTS(primask);
assert(*refcnt > 0);
if (--(*refcnt) == 0) {
bitmask = 1 << g_da1469x_pd_desc[pd].pmu_sleep_bit;
CRG_TOP->PMU_CTRL_REG |= bitmask;
ret = 1;
}
__HAL_ENABLE_INTERRUPTS(primask);
return ret;
}