/**************************************************************************** * * Copyright 2020 Samsung Electronics All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 * * 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include <string.h> #include <stdio.h> #include "cmsis_os.h" #include "s5js100.h" #include "s5js100_type.h" #include "s5js100_pwr.h" #include "s5js100_pmusfr.h" #include "s5js100_pmip.h" #include "s5js100_cmu.h" #include "mbed_trace.h" #include "platform/mbed_wait_api.h" #include "platform/mbed_thread.h" #define TRACE_GROUP "PWR" #ifndef S5JS100_PWR_DBG_ON #define S5JS100_PWR_DBG_ON 0 #endif #define S5JS100_PWR_DBG if (S5JS100_PWR_DBG_ON) tr_info /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define OFF 0 #define ON 1 /**************************************************************************** * Private Data ****************************************************************************/ enum _PMU_CMD_ { PMU_CMD_RESET = 1, PMU_CMD_INIT, PMU_CMD_SLEEP, PMU_CMD_AUTO, PMU_CMD_SHORTSLEEP, }; static PORT_NUM alv_wkup_src; static FILTER_TYPE wkup_type; /**************************************************************************** * Private Functions ****************************************************************************/ static void hw_delay_us(unsigned int Value) { volatile unsigned i, j; for (i = 0; i < (Value * 2); i++) for (j = 0; j < 100; j++); } #ifdef PRINT_NBSLEEP_LOG static void direct_uart(char *buf, unsigned int buf_len) { #if 0 unsigned int i; for (i = 0; i < buf_len; i++) { up_putc(buf[i]); } #endif } static void convert2hex(char *_buffer, unsigned int data) { // we make our string assuming all hex digits are 0 to 9 // string will be of the form 0xabcd // where a,b,c,d are the individual hex digits _buffer[0] = '0'; _buffer[1] = 'x'; _buffer[2] = ((data >> 28) & 0x0F) + '0'; _buffer[3] = ((data >> 24) & 0x0F) + '0'; _buffer[4] = ((data >> 20) & 0x0F) + '0'; _buffer[5] = ((data >> 16) & 0x0F) + '0'; _buffer[6] = ((data >> 12) & 0x0F) + '0'; _buffer[7] = ((data >> 8) & 0x0F) + '0'; _buffer[8] = ((data >> 4) & 0x0F) + '0'; _buffer[9] = ((data) & 0x0F) + '0'; _buffer[10] = '\n'; // now we correct for the case where a digit // is A to F: if (_buffer[2] > '9') { _buffer[2] += 7; } if (_buffer[3] > '9') { _buffer[3] += 7; } if (_buffer[4] > '9') { _buffer[4] += 7; } if (_buffer[5] > '9') { _buffer[5] += 7; } if (_buffer[6] > '9') { _buffer[6] += 7; } if (_buffer[7] > '9') { _buffer[7] += 7; } if (_buffer[8] > '9') { _buffer[8] += 7; } if (_buffer[9] > '9') { _buffer[9] += 7; } } #endif /**************************************************************************** * Public Functions ****************************************************************************/ extern char *get_env(const char *name); extern int s5js100_dcxo_initialize(void); MCPU_MODE mcpu_device_mode = MCPU_NONE; void pmu_sys_cdc_clk_en(int poweron) { modifyreg32(PMU_SYS_DCGOVRD, 1 << 1, poweron << 1); } void pmu_alive_cdc_clk_en(int poweron) { modifyreg32(PMU_ALIVE_DCGOVRD, 1 << 1, poweron << 1); } void mcpu_bin_loading(unsigned int tcm_base_addr, unsigned int flash_base_addr, unsigned int size) { memcpy((void *)tcm_base_addr, (void *)flash_base_addr, size); /* Select one cache invaildate function from below */ SCB_CleanInvalidateDCache_by_Addr((void *)tcm_base_addr, size); //SCB_CleanInvalidateDCache(); } unsigned int get_pmu_sys_mcpu_core_status(void) { return getreg32(PMU_SYS_MCPUMON); } void pmu_sys_asyncbus_mask(PWR_BLK eblock) { unsigned int val; val = getreg32(PMU_SYS_PASMASK); putreg32(val & (~eblock), PMU_SYS_PASMASK); } void pmu_sys_asyncbus_unmask(PWR_BLK eblock) { modifyreg32(PMU_SYS_PASMASK, 0x3, eblock); } void pmu_sys_reset(PWR_BLK eblock, int reset) { if (reset == 1) { putreg32(getreg32(PMU_SYS_IPRST_N) & (~eblock), PMU_SYS_IPRST_N); putreg32(getreg32(PMU_SYS_CMURST_N) & (~eblock), PMU_SYS_CMURST_N); } else { modifyreg32(PMU_SYS_CMURST_N, 0x3, eblock); hw_delay_us(1); modifyreg32(PMU_SYS_IPRST_N, 0x3, eblock); hw_delay_us(1); } } unsigned int get_temp(void) { unsigned int i, CurTemp = 0; putreg32(0x201f2, PMU_ALIVE_ROTSU_CFG1); putreg32((1u << 29) + (1u << 28), PMU_ALIVE_ROTSU_CFG0); for (i = 0; i < 10; i++) { putreg32((1u << 29) + (1u << 28) + (1u << 24), PMU_ALIVE_ROTSU_CFG0); hw_delay_us(120); putreg32((1u << 29) + (1u << 28) + (0u << 24), PMU_ALIVE_ROTSU_CFG0); hw_delay_us(10); CurTemp += getreg32(PMU_ALIVE_ROTSU_CFG0) & 0x7ff; putreg32(0, PMU_ALIVE_ROTSU_CFG0); } return CurTemp / 10; } unsigned int get_rotsu_value(int count) { int i, value = 0; putreg32(0x201f2, PMU_ALIVE_ROTSU_CFG1); putreg32((1u << 29) + (1u << 28), PMU_ALIVE_ROTSU_CFG0); for (i = 0; i < count; i++) { putreg32((1u << 29) + (1u << 28) + (1u << 24), PMU_ALIVE_ROTSU_CFG0); hw_delay_us(120); putreg32((1u << 29) + (1u << 28) + (0u << 24), PMU_ALIVE_ROTSU_CFG0); hw_delay_us(10); value += getreg32(PMU_ALIVE_ROTSU_CFG0) & 0x7ff; putreg32(0, PMU_ALIVE_ROTSU_CFG0); } return value; } int rotsu_get_temperature(void) { int adc, celcius; adc = get_rotsu_value(10); celcius = ((100 * adc - 683040) * 100) / 680; return (celcius + 50000); } bool mcpu_is_on(void) { if (getreg32(SYS_CFG_MCPU_HALTN) == 0x0) { return false; } else { return true; } } void mcpu_reset(void) { mcpu_device_mode = MCPU_NONE; /* if off, do nothing */ if (getreg32(SYS_CFG_MCPU_HALTN) == 0x0) { return; } putreg32(0x0, SYS_CFG_MCPU_HALTN); pmu_sys_cdc_clk_en(ON); putreg32(0x0, PMU_SYS_LH_PSTATE); putreg32(0xf, PMU_SYS_LH_PREQ); while (getreg32(PMU_SYS_LH_PACCEPT) != 0xF); putreg32(0x0, PMU_SYS_LH_PREQ); while (getreg32(PMU_SYS_LH_PACCEPT) != 0x0); pmu_sys_asyncbus_mask(RFIP_BLK + MCPU_BLK); pmu_sys_reset(RFIP_BLK + MCPU_BLK, 1); pmu_sys_cdc_clk_en(OFF); } void s5js100_pmu_init(void); void mcpu_init(MCPU_MODE device) { mcpu_reset(); s5js100_pmu_init(); putreg32(0x1, SYS_CFG_LH_RCG_EN); putreg32(0x2, SYS_CFG_PPMU); putreg32(0x0, SYS_CFG_PAS); putreg32(0x40000, BAAW_SRC_START_ADDR_0); putreg32(0x41000, BAAW_SRC_END_ADDR_0); putreg32(0x40000, BAAW_REMAPPED_ADDR_0); putreg32(0x80000003, BAAW_ACCESS_ENABLE_OF_WINDOW_0); putreg32(0x10000, BAAW_SRC_START_ADDR_1); putreg32(0x10010, BAAW_SRC_END_ADDR_1); putreg32(0x10000, BAAW_REMAPPED_ADDR_1); putreg32(0x80000003, BAAW_ACCESS_ENABLE_OF_WINDOW_1); putreg32(0x85022, BAAW_SRC_START_ADDR_2); putreg32(0x85042, BAAW_SRC_END_ADDR_2); putreg32(0x85022, BAAW_REMAPPED_ADDR_2); putreg32(0x80000003, BAAW_ACCESS_ENABLE_OF_WINDOW_2); putreg32(0x82021, BAAW_SRC_START_ADDR_3); putreg32(0x82022, BAAW_SRC_END_ADDR_3); putreg32(0x85022, BAAW_REMAPPED_ADDR_3); putreg32(0x80000003, BAAW_ACCESS_ENABLE_OF_WINDOW_3); mcpu_bin_loading(TCM_BASE, (device == MCPU_CP) ? CP_BIN_BASE : GNSS_BIN_BASE, MCPU_BINARY_SIZE); switch (device) { case MCPU_CP: putreg32(0x01000001, 0x82000A04); //SCMU_MR_REGISTER_A04 putreg32(0x0, SYS_CFG_CELLULAR_GNSS_SEL); putreg32(0x1, PMU_SYS_ABX_PWRCTRL); break; case MCPU_GNSS: putreg32(0x01020001, 0x82000A04); //SCMU_MR_REGISTER_A04 putreg32(0x1, SYS_CFG_CELLULAR_GNSS_SEL); putreg32(0xe7, PMU_SYS_ABX_PWRCTRL); break; default: #if defined (__ARMCC_VERSION) while (1); #else asm("b ."); #endif break; } /* DEVICE DEPENDENT CODE!!! WRONG PALCE!!! Move it in Users application or device init section. // 4. Pin mux chagen for RF signal modifyreg32(0x8202110c, 0x7, 0x2); //XGPIO3, when 0x2, PA_MODE0 is selected. modifyreg32(0x82021110, 0x7, 0x2); //XGPIO4, when 0x2, PA_MODE1 is selected. modifyreg32(0x82021114, 0x7, 0x2); //XGPIO5, when 0x2, PA_ON0 is selected. modifyreg32(0x82021118, 0x7, 0x2); //XGPIO6, when 0x2, PA_ON1 is selected.. */ // 5. Buck boost turn on. s5js100_pmip_control_BuckBooster(1); // 6. mcpu run putreg32(0x1, SYS_CFG_MCPU_HALTN); // PAS_ICG_DIS=0, PAS_PREQ_QUICK_STARTn=0 S5JS100_PWR_DBG("\nMCPU BOOT\n"); if (device == MCPU_GNSS) { putreg32(0x7004, 0x8202114C); putreg32(0x7004, 0x82021150); putreg32(0x7004, 0x82021154); putreg32(0x7004, 0x82021158); putreg32(0x7004, 0x8202115C); S5JS100_PWR_DBG("\nXANT_SW change\n"); putreg32(0x4107, 0x82021088); putreg32(0x4107, 0x8202108C); putreg32(0x4107, 0x82021090); S5JS100_PWR_DBG("\nGPIO Out enable\n"); putreg32(0x00001000, 0x85026104); putreg32(0xFFFFc7FF, 0x85026004); putreg32(0x6303, 0x8202111c); putreg32(0x100, 0x820009e0); } else { S5JS100_PWR_DBG("\nSetting FOR CP\n"); putreg32(0x0, 0x8504101C); putreg32(0x0, 0x8504120C); putreg32(0x8, 0x8504120C); putreg32(0xf9f4f44, 0x85041208); putreg32(0xC, 0x8504120C); /* check if eFUSE ABX value available */ if (getreg32(0x82010500) != 0x0) { S5JS100_PWR_DBG("ABX CAL, with EFUSE..(0x%x)\n", getreg32(0x82010500)); putreg32(getreg32(0x82010500) & 0x3FF, 0x85041204); putreg32(0x09, 0x8504120C); S5JS100_PWR_DBG("0x8504120C=0x%x\n", getreg32(0x8504120C)); S5JS100_PWR_DBG("ABX value : 0x%8X\n", getreg32(0x85041204)); S5JS100_PWR_DBG("0x85041200 value : 0x%8X\n", getreg32(0x85041200)); S5JS100_PWR_DBG("0x85041204 value : 0x%8X\n", getreg32(0x85041204)); S5JS100_PWR_DBG("0x85041208 value : 0x%8X\n", getreg32(0x85041208)); S5JS100_PWR_DBG("0x8504120C value : 0x%8X\n", getreg32(0x8504120C)); } else { S5JS100_PWR_DBG("ABX AUTO CAL\n"); S5JS100_PWR_DBG("Check 1st register\n"); S5JS100_PWR_DBG("0x85041200 value : 0x%8X\n", getreg32(0x85041200)); S5JS100_PWR_DBG("0x85041204 value : 0x%8X\n", getreg32(0x85041204)); S5JS100_PWR_DBG("0x85041208 value : 0x%8X\n", getreg32(0x85041208)); S5JS100_PWR_DBG("0x8504120C value : 0x%8X\n", getreg32(0x8504120C)); putreg32(getreg32(0x85041204) | (1 << 10), 0x85041204); putreg32(getreg32(0x8504120C) | (1 << 2), 0x8504120C); //while (!(getreg32(0x85041200) & (1 << 10))); //wait_ms(10); thread_sleep_for(10); putreg32(getreg32(0x85041200) & 0x3FF, 0x85041204); if ((getreg32(0x85041204) & 0x3FF) == 0x0) { putreg32(0x210, 0x85041204); } S5JS100_PWR_DBG("Check 2nd register\n"); S5JS100_PWR_DBG("ABX value : 0x%8X\n", getreg32(0x85041204)); S5JS100_PWR_DBG("0x85041200 value : 0x%8X\n", getreg32(0x85041200)); S5JS100_PWR_DBG("0x85041204 value : 0x%8X\n", getreg32(0x85041204)); S5JS100_PWR_DBG("0x85041208 value : 0x%8X\n", getreg32(0x85041208)); S5JS100_PWR_DBG("0x8504120C value : 0x%8X\n", getreg32(0x8504120C)); } S5JS100_PWR_DBG("ABX CAL, DAC..\n"); putreg32(0x0D, 0x8504120C); S5JS100_PWR_DBG("r_USIM0_DATA pull up enable..\n"); modifyreg32(0x82021040, (0x3 << 11), (0x3 << 11)); } modifyreg32(PMU_SYS_ABX_PWRCTRL, 0x1, 0x0); if (device == MCPU_CP) { S5JS100_PWR_DBG("PMU DAC PWR control..\n"); modifyreg32(PMU_SYS_DAC_PWRCTRL, 0x1, 0x0); S5JS100_PWR_DBG("NSLEEP Enable / don't update re-calculated counter value..\n"); modifyreg32(PMU_ALIVE_ALVSYSCTRLCFG, (0x7 << 7), (0x7 << 7)); putreg32((1u << 2) + (1u << 0) + (1u << 1), PMU_ALIVE_NSLEEP_CFG); // [0] :wakeup interrupt enable , [1]: sys_valid_int_en S5JS100_PWR_DBG("CP boot done..\n"); } mcpu_device_mode = device; } void s5js100_sw_powerdomain_off(PWR_BLK eblock) { pmu_sys_cdc_clk_en(ON); if (eblock & MCPU_BLK) { modifyreg32(PMU_SYS_ABX_PWRCTRL, 0x1, 0x1); modifyreg32(PMU_SYS_DAC_PWRCTRL, 0x1, 0x1); if (getreg32(PMU_SYS_IPRST_N)) { putreg32(0x0, PMU_SYS_LH_PSTATE); putreg32(0xf, PMU_SYS_LH_PREQ); while (getreg32(PMU_SYS_LH_PACCEPT) != 0xF); putreg32(0x0, PMU_SYS_LH_PREQ); while (getreg32(PMU_SYS_LH_PACCEPT) != 0x0) ; } } hw_delay_us(1); pmu_sys_asyncbus_mask(eblock); pmu_sys_reset(eblock, 1); pmu_sys_cdc_clk_en(OFF); } void s5js100_sw_powerdomain_on(PWR_BLK eblock) { pmu_sys_cdc_clk_en(ON); pmu_sys_asyncbus_unmask(eblock); pmu_sys_reset(eblock, 0); if (eblock & MCPU_BLK) { putreg32(0xf, PMU_SYS_LH_PSTATE); putreg32(0xf, PMU_SYS_LH_PREQ); while (getreg32(PMU_SYS_LH_PACCEPT) != 0xF); putreg32(0x0, PMU_SYS_LH_PREQ); while (getreg32(PMU_SYS_LH_PACCEPT) != 0x0) ; } pmu_sys_cdc_clk_en(OFF); } unsigned int get_bootflag(void) { return !!(getreg32(PMU_ALIVE_BOOTFLAG) & 1); } void alive_gpio_cfg(PORT_NUM gpio, PORT_DIR cfg) { modifyreg32((uint32_t)PMU_ALIVE_GPIOALV0CTRL + 0x4 * gpio, (0x3 << 0), 1 << cfg); } void alive_gpio_set_value(PORT_NUM gpio, int value) { modifyreg32((uint32_t)PMU_ALIVE_GPIOALV0CTRL + 0x4 * gpio, (0x1 << 5), value << 5); } int alive_gpio_get_value(PORT_NUM gpio) { return ((getreg32((uint32_t)PMU_ALIVE_GPIOALV0CTRL + 0x4 * gpio) >> 4) & 0x1); } void gpio_set_pull(PORT_NUM gpio, PUD_CON mode) { modifyreg32((uint32_t)PMU_ALIVE_GPIOALV0CTRL + 0x4 * gpio, (0x3 << 2), mode << 2); } void gpio_eint_mask(PORT_NUM gpio) { modifyreg32((uint32_t)PMU_ALIVE_GPIOALV0CTRL + 0x4 * gpio, (1u << 9), 0x0 << 9); } void gpio_eint_unmask(PORT_NUM gpio) { modifyreg32((uint32_t)PMU_ALIVE_GPIOALV0CTRL + 0x4 * gpio, (1u << 9), 0x1 << 9); } int gpio_eint_is_pending(PORT_NUM gpio) { return ((getreg32((uint32_t)PMU_ALIVE_GPIOALV0CTRL + gpio * 0x4) >> 10) & 0x1); } void gpio_eint_clear_pending(PORT_NUM gpio) { modifyreg32((uint32_t)PMU_ALIVE_GPIOALV0CTRL + gpio * 0x4, (1u << 10), 1 << 10); } void gpio_eint_set_filter(PORT_NUM gpio, FILTER_TYPE type) { modifyreg32((uint32_t)PMU_ALIVE_GPIOALV0CTRL + 0x4 * gpio, (0x3 << 6), type << 6); } void sel_recalculate_clk(void) { if (getreg32(PMU_ALIVE_TCXODCXOSEL) == 0x2) { /* keep TCXO clk source */ modifyreg32(PMU_ALIVE_TCXODCXOSEL, (0x1 << 0), 1); } else { /* keep DCXO clk source */ } } bool is_TCXO(void) { if (getreg32(PMU_ALIVE_TCXODCXOSEL) & (0x1 << 1)) { return 1; } else { return 0; } } extern int external_pin; static int alvgpio_interrupt(void) { if (!strcmp(get_env("ALVTOGGLE"), "ON")) { external_pin = !external_pin; } else { //putreg8( '>' , 0x83014020); //putreg8( (external_pin&0xFF) + 0x30, 0x83014020); if (!((getreg32(0x810000f0 + alv_wkup_src * 0x4) & (1 << 4)) >> 4 ^ wkup_type)) { gpio_eint_set_filter(alv_wkup_src, !wkup_type); external_pin = 1; } else { gpio_eint_set_filter(alv_wkup_src, wkup_type); external_pin = 0; } //putreg8( (external_pin&0xFF) + 0x30, 0x83014020); //putreg8( '<' , 0x83014020); //putreg8( '\r' , 0x83014020); //putreg8( '\n' , 0x83014020); } gpio_eint_clear_pending(alv_wkup_src); putreg32(0x4, 0xE000E280); return 0; } void alvgpio_interrupt_enable(void) { if (alv_wkup_src != GPIO_ALIVE_NONE) { NVIC_SetVector(S5JS100_IRQ_PMU_AlivePad, (unsigned int)alvgpio_interrupt); #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) SCB_InvalidateICache(); #endif gpio_eint_unmask(alv_wkup_src); NVIC_EnableIRQ(S5JS100_IRQ_PMU_AlivePad); } } #define GPIO_ALIVE_CTRL(x) (0x810000f0 + (x) * 0x4) void alvgpio_init(void) { int toggle = 0; putreg32(0x0, GPIO_ALIVE_CTRL(0)); putreg32(0x0, GPIO_ALIVE_CTRL(1)); // Set GPIOx to be input pull down modifyreg32(GPIO_ALIVE_CTRL(0), (0x1 << 0x2), (0x1 << 0x2)); //Pullup/down enable modifyreg32(GPIO_ALIVE_CTRL(0), (0x1 << 0x3), (0x0 << 0x3)); //Input pull-down modifyreg32(GPIO_ALIVE_CTRL(1), (0x1 << 0x2), (0x1 << 0x2)); //Pullup/down enable modifyreg32(GPIO_ALIVE_CTRL(1), (0x1 << 0x3), (0x0 << 0x3)); //Input pull-down modifyreg32(0x85023088, 0x1, 0x1); // select wakeup source alv_wkup_src = GPIO_ALIVE_NONE; if (!strcmp(get_env("ALVTOGGLE"), "ON")) { toggle = 1; wkup_type = RISING; } else { wkup_type = HIGHLEVEL; } if (!strncmp(get_env("WAKEUP"), "ALVGPIO0", 8)) { alv_wkup_src = GPIO_ALIVE_0; } if (!strncmp(get_env("WAKEUP"), "ALVGPIO1", 8)) { alv_wkup_src = GPIO_ALIVE_1; } // GpioAlv Output enable if (!strcmp(get_env("ALVGPIO0"), "HIGH")) { modifyreg32(GPIO_ALIVE_CTRL(0), 0x21, 0x21); } if (!strcmp(get_env("ALVGPIO0"), "LOW")) { modifyreg32(GPIO_ALIVE_CTRL(0), 0xC, 0x4); } if (!strcmp(get_env("ALVGPIO1"), "HIGH")) { modifyreg32(GPIO_ALIVE_CTRL(1), 0x21, 0x21); } if (!strcmp(get_env("ALVGPIO1"), "LOW")) { modifyreg32(GPIO_ALIVE_CTRL(1), 0xC, 0x4); } if (alv_wkup_src != GPIO_ALIVE_NONE) { // wkup src trigger if (get_env("WAKEUP")[8] == 'H') { wkup_type = toggle ? RISING : HIGHLEVEL; } if (get_env("WAKEUP")[8] == 'L') { wkup_type = toggle ? FALLING : LOWLEVEL; } // wkup src Input enable modifyreg32(GPIO_ALIVE_CTRL(alv_wkup_src), 0x2, 0x2); if (toggle) { gpio_eint_set_filter(alv_wkup_src, wkup_type); } else { if (!((getreg32(0x810000f0 + alv_wkup_src * 0x4) & (1 << 4)) >> 4 ^ wkup_type)) { gpio_eint_set_filter(alv_wkup_src, !wkup_type); external_pin = 1; } else { gpio_eint_set_filter(alv_wkup_src, wkup_type); external_pin = 0; } } } // interrupt clear at PMU_ALIVE gpio_eint_clear_pending(GPIO_ALIVE_0); gpio_eint_clear_pending(GPIO_ALIVE_1); // pending clear AlivePadInt putreg32(0x4, 0xE000E280); } void s5js100_pmu_init(void) { unsigned int pmu_alive_slpclk_cfg = 1; #if USE_SLOW_CLOCK_TCXO pmu_alive_slpclk_cfg = 0; #else if (!strncmp(get_env("BOARD"), "0.2", 3)) { pmu_alive_slpclk_cfg = 0; } #endif alvgpio_init(); alvgpio_interrupt_enable(); if (get_bootflag() == COLD_BOOT) { putreg32(pmu_alive_slpclk_cfg, PMU_ALIVE_SLPCLK_CFG); putreg32((1u << 0) + (3u << 4) + (3u << 8) + (3u << 12) + (4u << 16) + (5u << 20) + (5u << 24) + (6u << 28), PMU_ALIVE_PMIPCTRL_ONTIMING); putreg32((1u << 0) + (1u << 4) + (1u << 8), PMU_ALIVE_PMIPCTRL_OFFTIMING); putreg32((4u << 10) + (4u << 6), PMU_ALIVE_DCXOCTRL_CFG0); sel_recalculate_clk(); putreg32(0x001, PMU_ALIVE_RECALC_CFG2); putreg32((1u << 31) + (2u << 0), PMU_ALIVE_RECALC_CFG3); } else { putreg32(0x0, PMU_ALIVE_SYSOFF); putreg32(pmu_alive_slpclk_cfg, PMU_ALIVE_SLPCLK_CFG); if ((getreg32(PMU_ALIVE_WKUPINTSNAPSHOT) & GPIO_ALV0) > 0) { //putreg32((1u << 10), PMU_ALIVE_GPIOALV0CTRL); //putreg32((1u << 2) + (1u << 1), PMU_ALIVE_GPIOALV0CTRL); } if ((getreg32(PMU_ALIVE_WKUPINTSNAPSHOT) & GPIO_ALV1) > 0) { //putreg32((1u << 10), PMU_ALIVE_GPIOALV1CTRL); //putreg32((1u << 3) + (1u << 2) + (1u << 1), PMU_ALIVE_GPIOALV1CTRL); } if ((getreg32(PMU_ALIVE_WKUPINTSNAPSHOT) & APSLPCNT) > 0) { modifyreg32(PMU_ALIVE_APSLPCNT_CFG0, (1u << 3), 0 << 3); putreg32(0, PMU_ALIVE_APSLPCNT_CFG0); } if ((getreg32(PMU_ALIVE_WKUPINTSNAPSHOT) & NSLEEP) > 0) { //do what? } if ((getreg32(PMU_ALIVE_WKUPINTSNAPSHOT) & TEMPMON) > 0) { modifyreg32(PMU_ALIVE_TSUINT, (1u << 3), 1 << 3); } } putreg32(0xf21, PMU_ALIVE_PMIPCTRL_OFFTIMING); s5js100_sw_powerdomain_on(MCPU_BLK + RFIP_BLK); putreg32(0x0, PMU_SYS_PROFILERCTRL); putreg32((1u << 2), PMU_SYS_PROFILERCTRL); s5js100_pmip_initialize(); s5js100_dcxo_initialize(); } //#define SLPCLK 0.031941f // MHz ..26/DCXO(814) //#define SLPCLK_DIV 814 // MHz ..26/DCXO(814) #define SLPCLK 0.032786f // MHz ..26/DCXO(793) #define SLPCLK_DIV 793 // MHz ..26/DCXO(793) void set_sleep_counter(unsigned int time_ms) { unsigned int count; count = (unsigned int)(time_ms * SLPCLK * 1000); putreg32(count >> 20, PMU_ALIVE_APSLPCNT_CFG1); putreg32(count & 0xfffff, PMU_ALIVE_APSLPCNT_CFG2); } void conver_nbsleep_to_aspsleep_counter(unsigned int nbsleep_cnt) { putreg32(nbsleep_cnt >> 20, PMU_ALIVE_APSLPCNT_CFG1); putreg32(nbsleep_cnt & 0xfffff, PMU_ALIVE_APSLPCNT_CFG2); } void enable_counter(void) { if (getreg32(PMU_ALIVE_APSLPCNT_CFG0) & (0x1 << 1)) { //lldbg("APSLP cnt already enabled\n"); } else { putreg32(0, PMU_ALIVE_APSLPCNT_CFG1); putreg32(1, PMU_ALIVE_APSLPCNT_CFG2); putreg32(0x3, PMU_ALIVE_APSLPCNT_CFG0); putreg32((15u << 6) + (1u << 3) + (0u << 2) + (1u << 1) + (1u << 0), PMU_ALIVE_APSLPCNT_CFG0); } } void disable_counter(void) { putreg32(0x3, PMU_ALIVE_APSLPCNT_CFG0); } long long get_counter(void) { long long current_count = 0; current_count = getreg32(PMU_ALIVE_APSLPCNT_CFG3) << 20; current_count |= getreg32(PMU_ALIVE_APSLPCNT_CFG4); S5JS100_PWR_DBG("H_COUNT : 0x%x ,L_COUNT : 0x%x ,\n", (unsigned int)(0xffff & (current_count >> 20)), (unsigned int)(0xfffff & current_count)); return current_count; } void s5js100_pmu_sleep(PMU_SLEEP_INFO *info) { PMU_SLEEP_INFO *pmu_info = (PMU_SLEEP_INFO *)((uintptr_t) info); unsigned int cur_temp; unsigned int _wkupsrc = pmu_info->wakeup_src; putreg32(0x0, 0xe000e010); //disable tick , not to interrupt in wfi state putreg32(0x02000000, 0xe000ed04); //Pend SysTick clear putreg32(0xFFFFFFFF, 0xe000e180); //IRQ0_31 disable putreg32(0xFFFFFFFF, 0xe000e184); //IRQ32_64 disable putreg32(0xFFFFFFFF, 0xe000e280); //IRQ0_31_PEN clear putreg32(0xFFFFFFFF, 0xe000e284); //IRQ32_64_PEN clear putreg32(0, PMU_ALIVE_SLPCLK_CFG); //SwMuxSel=0 1: SwSel selects sleep clock mux //disable SMC clockgating putreg32(0x200000, MIFCMU_CLK_CON_GAT_GOUT_MIF_UID_SMC_IPCLKPORT_CLK); putreg32(0x200000, MIFCMU_CLK_CON_GAT_GOUT_MIF_UID_SMC_IPCLKPORT_L2CLK); /* * (0) sleep or not * * Before sleep, SW must check sleep counters * if sleep counter reaches 0 soon, :if expand("%") == ""|browse confirm w|else|confirm w|endif * SW should skip sleep and just waits for wakeup interrupt * * (1) SW-controlled Power Domain Off * SW domain power off done */ #if defined(__ICCARM__) #warning "no support s5js100_sw_powerdomain_off" #else s5js100_sw_powerdomain_off(RFIP_BLK + MCPU_BLK); #endif S5JS100_PWR_DBG("1)SW domain power off\n"); /* * PMU, CMU initial setting */ putreg32(0x3, PMU_ALIVE_PROFILERCTRL); // BlackboxClr before sleep, BlackboxEn=1 putreg32(0x3, PMU_SYS_PROFILERCTRL); // BlackboxClr before sleep, BlackboxEn=1 putreg32((1u << 30) + (2u << 26) + (6u << 22), PMU_ALIVE_ALVSYSCTRLCFG); //CntLmtPWR_OFF_MIN=1,CntLmtPWR_STABLE=2(fast sim), CntLmtCKCHG=6 // PMU_ALIVE_AlvSysCtrlCfg = (1u<<30)+(2u<<26)+(6u<<22)+(1u<<8);//CntLmtPWR_OFF_MIN=1,CntLmtPWR_STABLE=2(fast sim), CntLmtCKCHG=6, ReCalcUpDis=1 S5JS100_PWR_DBG("2)PMU, CMU initial setting\n"); putreg32((1u << 8) + (2u << 4), PMU_ALIVE_TEMPMON_CFG0); //CntLmt_INTERVAL=1, CntLmt_TSU_CLK=2 cur_temp = get_temp(); S5JS100_PWR_DBG("2.1)current temperature : 0x%x\n", cur_temp); putreg32(cur_temp, PMU_ALIVE_TEMPMON_CFG1); putreg32(7, PMU_ALIVE_TEMPMON_CFG2); //Delta=30 // PMU_ALIVE_TsuInt = (1u<<0) + (1u<<1); //UpIntEn=1, LowIntEn=1 : tested in TempMon wakeup putreg32(30, PMU_ALIVE_RECALC_CFG1); //RcoRefCnt=3 ==> (30*4 + 1)ea RCO counted in RCO_TIMER_COMPENSATION putreg32((SLPCLK_DIV * ((30 * 4) + 1)), PMU_ALIVE_RECALC_CFG0); //InitialDcxoRefCnt = (DCXO/814)*121 /* * Wakeup source selection */ S5JS100_PWR_DBG("3)Wakeup source selection\n"); putreg32(_wkupsrc, PMU_ALIVE_WKUPSRCEN); // wakeup source enable // it is just for simulation. // in real SW, they are all enabled. if ((_wkupsrc & GPIO_ALV0) > 0) { //active high gpio_eint_clear_pending(GPIO_ALIVE_0); putreg32(0x4, 0xE000E280); } if ((_wkupsrc & GPIO_ALV1) > 0) { //active low, pulse type gpio_eint_clear_pending(GPIO_ALIVE_1); putreg32(0x4, 0xE000E280); } if ((_wkupsrc & APSLPCNT) > 0) { // AP sleep counter setting putreg32((1u << 5), PMU_ALIVE_APSLPCNT_CFG0); //5 : interrupt pending clear , 0: SW reset. set_sleep_counter(pmu_info->time_msec); S5JS100_PWR_DBG("PMU_ALIVE_APSLPCNT_CFG1 : 0x%x\n", *(unsigned int *)PMU_ALIVE_APSLPCNT_CFG1); S5JS100_PWR_DBG("PMU_ALIVE_APSLPCNT_CFG2 : 0x%x\n", *(unsigned int *)PMU_ALIVE_APSLPCNT_CFG2); putreg32((15u << 6) + (1u << 3) + (0u << 2) + (1u << 1) + (1u << 0), PMU_ALIVE_APSLPCNT_CFG0); } if ((_wkupsrc & NSLEEP) > 0) { modifyreg32(PMU_ALIVE_ALVSYSCTRLCFG, (0x7 << 7), (0x7 << 7)); putreg32((1u << 2) + (1u << 0) + (1u << 1), PMU_ALIVE_NSLEEP_CFG); } if ((_wkupsrc & TEMPMON) > 0) { putreg32(((cur_temp + pmu_info->upper_temp) << 16) + ((cur_temp - pmu_info->lower_temp) << 0), PMU_ALIVE_TEMPMON_CFG3); putreg32((1u << 0) + (1u << 1), PMU_ALIVE_TSUINT); } S5JS100_PWR_DBG("PD CDC clock on \n"); // SMC LPI lock putreg32((1u << 1), PMU_SYS_DCGOVRD); putreg32(0, PMU_SYS_SMC_LPI); while ((getreg32(PMU_SYS_SMC_LPI) & 0x6) != 0x0) ; putreg32(0x0, PMU_SYS_DCGOVRD); S5JS100_PWR_DBG("PD CDC clock off \n"); putreg32(0x1, PMU_ALIVE_SYSOFF); putreg32(0x1, PMU_SYS_SYSCTRL_SYSOFF); while ((getreg32(0x85024018) >> 3) & 0x1) ; modifyreg32(PMU_ALIVE_PMIP_TCXO_LDO, 0x80000000, 0x0); // PMU STAUTS to be WARM BOOT putreg32(WAKEUP_BOOT, PMU_ALIVE_BOOTFLAG); #if defined( __GNUC__ ) asm("dmb"); asm("wfi"); #else __DMB(); __WFI(); #endif while (1) ; } void pmu_nb_sleep(void) { PMU_SLEEP_INFO pmu_info; pmu_info.wakeup_src = NSLEEP; if (alv_wkup_src == GPIO_ALIVE_0) { pmu_info.wakeup_src |= GPIO_ALV0; } if (alv_wkup_src == GPIO_ALIVE_1) { pmu_info.wakeup_src |= GPIO_ALV1; } pmu_info.alv0_gpio_int_type = wkup_type; s5js100_pmu_sleep((PMU_SLEEP_INFO *)&pmu_info); } void __attribute__((section(".ramfunc"))) s5js100_sflash_deepsleep(int on) { volatile unsigned int count = 2000; if (on) { modifyreg32(0x85020024, 0xFF000000, 0xB9000000); putreg8(1, 0x8502005A); } else { modifyreg32(0x85020024, 0xFF000000, 0xAB000000); putreg8(1, 0x8502005A); } while (count--) { #if defined( __GNUC__ ) asm("nop"); #else __NOP(); #endif } } #if !defined(__ICCARM__) void __attribute__((section(".ramfunc"))) s5js100_enter_exit_idle_drx(void) { #if defined( __GNUC__ ) asm("isb"); asm("dsb"); s5js100_sflash_deepsleep(1); asm("dmb"); asm("wfi"); s5js100_sflash_deepsleep(0); asm("isb"); asm("dsb"); #else __ISB() __DSB(); s5js100_sflash_deepsleep(1); __DMB(); __WFI(); s5js100_sflash_deepsleep(0); __ISB() __DSB(); #endif } #endif #if !defined(__ICCARM__) void __attribute__((section(".ramfunc"))) pmu_sleep_isr(void) { #if defined(__ARMCC_VERSION) while (1); #else asm("b ."); #endif } #endif char nbsleep_msg[] = "NBSLEEP CNT LSB=>"; char ldo6_on[] = "LDO6 ON"; char ldo6_off[] = "LDO6 OFF"; /****************** Option Table ***********************/ //#define PRINT_NBSLEEP_LOG #define LDO_PWR_ON 0 // possible option : 1/1/1, 1/0/1, 1/1/0, 1/0/0, 0/0/0 #define DCXO_IP_ON 0 #define BUS_CLK_26MHZ 0 #define SLEEP_CLK_32KHZ 0 #define PLL_OFF_OSC //always on //#define LDO6_PWR_ALWAYS_ON #define LDO1_ON 0 #define LDO2_ON 0 #define LDO4_ON 0 /*******************************************************/ extern int cal_init(void); extern int s5js100_dcxo_force_initialize(void); extern void s5js100_sflash_reinit(void); /* BASE : condition 3 */ /* default - DCXO_IP_ON */ /* - BUS Clock 26MHz */ /* - Sleep Clock 32Khz */ //void __attribute__((section(".ramfunc"))) pmu_short_sleep(void) void pmu_short_sleep(void) { unsigned int i; PMU_SLEEP_INFO pmu_info; unsigned int nvic[8]; #ifdef PRINT_NBSLEEP_LOG char __buffer[12]; #endif #ifdef PRINT_NBSLEEP_LOG convert2hex(__buffer, getreg32(0x81000000 + 0xE8)); direct_uart(nbsleep_msg, sizeof(nbsleep_msg)); direct_uart(__buffer, sizeof(__buffer)); #endif pmu_info.wakeup_src = NSLEEP; if (alv_wkup_src == GPIO_ALIVE_0) { pmu_info.wakeup_src |= GPIO_ALV0; } if (alv_wkup_src == GPIO_ALIVE_1) { pmu_info.wakeup_src |= GPIO_ALV1; } //BUCK1, LDO0 on modifyreg32(PMU_ALIVE_PMIPCTRL_OVRD, (1u << 0) + (1u << 1) + (1u << 7) + (1u << 8), (1u << 0) + (1u << 1) + (0 << 7) + (0 << 8)); //BUCK0, LDO3 on modifyreg32(PMU_ALIVE_PMIPCTRL_OVRD, (1u << 4) + (1u << 6) + (1u << 11) + (1u << 13), (1u << 4) + (1u << 6) + (0 << 11) + (0 << 13)); /* LDO 1/2/4 ON/OFF */ // LDO1 ON/OFF modifyreg32(PMU_ALIVE_PMIPCTRL_OVRD, (1u << 2), LDO1_ON << 2); // LDO2 ON/OFF modifyreg32(PMU_ALIVE_PMIPCTRL_OVRD, (1u << 3), LDO2_ON << 3); // LDO4 ON/OFF modifyreg32(PMU_ALIVE_PMIPCTRL_OVRD, (1u << 5), LDO4_ON << 5); pmu_sys_cdc_clk_en(ON); //CDC ON putreg32((1u << 30) + (2u << 26) + (6u << 22), PMU_ALIVE_ALVSYSCTRLCFG); modifyreg32(PMU_ALIVE_ALVSYSCTRLCFG, 0x3ff, 0x3ff); modifyreg32(PMU_ALIVE_ALVSYSCTRLCFG, 1u << 6, 0); // do not enable clamp cells // Do PMIP off modifyreg32(PMU_ALIVE_ALVSYSCTRLCFG, 1u << 3, LDO_PWR_ON << 3); modifyreg32(PMU_ALIVE_ALVSYSCTRLCFG, (1u << 2), (DCXO_IP_ON << 2)); //DCXO_IP modifyreg32(PMU_ALIVE_ALVSYSCTRLCFG, (1u << 1), (BUS_CLK_26MHZ << 1)); //BUS Clock 26MHZ modifyreg32(PMU_ALIVE_ALVSYSCTRLCFG, (1u << 0), (SLEEP_CLK_32KHZ << 0)); //sleep_clock 32KHz putreg32((1u << 1) + (1u << 0), PMU_ALIVE_PROFILERCTRL); //BlackboxClr=1, BlackboxEn=1 putreg32((1u << 1) + (1u << 0), PMU_SYS_PROFILERCTRL); //BlackboxClr=1, BlackboxEn=1 putreg32((1u << 1), PMU_SYS_ACPUCTRL_CFG0); //EfuseSenseReqDis=1 putreg32(pmu_info.wakeup_src, PMU_ALIVE_WKUPSRCEN); modifyreg32(PMU_ALIVE_ALVSYSCTRLCFG, (0x7 << 7), (0x7 << 7));// [0] :wakeup interrupt enable , [1]: sys_valid_int_en putreg32((1u << 2) + (1u << 0) + (1u << 1), PMU_ALIVE_NSLEEP_CFG);// [0] :wakeup interrupt enable , [1]: sys_valid_int_en if ((pmu_info.wakeup_src & APSLPCNT) > 0) { // AP sleep counter setting putreg32((1u << 5), PMU_ALIVE_APSLPCNT_CFG0);//5 : interrupt pending clear , 0: SW reset. set_sleep_counter(pmu_info.time_msec); putreg32((15u << 6) + (1u << 3) + (0u << 2) + (1u << 1) + (1u << 0), PMU_ALIVE_APSLPCNT_CFG0); //IgnoreReCalcLmt=15,IntMask_n=1,ReCalcUpdateMask=0,En=1,SwRst_n=1 } for (i = 0; i < 8; i++) { nvic[i] = getreg32(0xE000E100 + (0x4 * i)); putreg32(nvic[i], 0xE000E180 + (0x4 * i));//disable all interrupt } NVIC_EnableIRQ(S5JS100_IRQ_SLEEP); //NBSLEEP alvgpio_interrupt_enable(); while (getreg32(PMU_SYS_MCPUMON) != 1); // waits for MCPU WFI putreg32(0x4, 0xE000E280); while (getreg32(0xE000E200 + 0x4) & (0x80)) { putreg32(0x80, 0xE000E284);//clear SLEEP pending, should be clear by CP first } #ifdef PRINT_NBSLEEP_LOG convert2hex(__buffer, getreg32(PMU_ALIVE_ALVSYSCTRLCFG)); direct_uart(__buffer, sizeof(__buffer)); #endif // PMU could control on/off of LDO6 if LDO6 SwMuxSel was zero. // PMU power off LDO6 modifyreg32(PMU_ALIVE_PMIP_TCXO_LDO, 0x80000000, 0x0); *(volatile unsigned *)(0x82000100) = 0x018c0d01; //system mux change PLL -> DCXO, PLL off putreg32(0x1, PMU_ALIVE_SYSOFF); putreg32(0x1, PMU_SYS_SYSCTRL_SYSOFF); putreg32(1, PMU_ALIVE_BOOTFLAG); #if defined(__ICCARM__) #warning "ICCARM no support idle drx" #else s5js100_enter_exit_idle_drx(); //cpu wfi #endif while (getreg32(PMU_SYS_ACPUCTRL_FSM) != 0x14151600 && getreg32(PMU_SYS_ACPUCTRL_FSM) != 0); /* DCXO ON */ if (!(DCXO_IP_ON)) { s5js100_dcxo_force_initialize(); /* DCXO off -> on after short_sleep */ } cal_init(); s5js100_sflash_reinit(); putreg32(0x0, PMU_ALIVE_SYSOFF); putreg32(0x0, PMU_SYS_SYSCTRL_SYSOFF); /* AP2CP mailbox interrupt generation */ /* MCPU back to work */ putreg32(0x3F80, PMU_ALIVE_PMIPCTRL_OVRD); //wait MCPU wfi break // while(PMU_SYS_McpuMon == 0x1); putreg32(0x2, 0x85023020); putreg32(0x2, 0x8502301C); for (i = 0; i < 8; i++) { putreg32(nvic[i], 0xE000E100 + (0x4 * i)); //enable all interrupt } NVIC_DisableIRQ(S5JS100_IRQ_SLEEP); pmu_sys_cdc_clk_en(OFF); //CDC OFF gpio_eint_mask(alv_wkup_src); //IntEn=1 alvgpio_interrupt_enable(); } void pmu_test_auto(int sleep_time_msec) { PMU_SLEEP_INFO pmu_info; S5JS100_PWR_DBG("%s...\n", __func__); pmu_info.wakeup_src = APSLPCNT; pmu_info.time_msec = sleep_time_msec; s5js100_pmu_sleep((PMU_SLEEP_INFO *)&pmu_info); } unsigned int get_wakeup_src_status(void) { return getreg32(PMU_ALIVE_WKUPINTSNAPSHOT); } void mcpu_reset_dump(void) { mcpu_reset(); s5js100_pmu_init(); *((unsigned int *)(TCM_BASE + 0x1800 - 0x40)) = 0x55500000; SCB_CleanDCache(); putreg32(0x1, SYS_CFG_MCPU_HALTN); }