diff options
Diffstat (limited to 'chip/npcx/clock.c')
-rw-r--r-- | chip/npcx/clock.c | 517 |
1 files changed, 0 insertions, 517 deletions
diff --git a/chip/npcx/clock.c b/chip/npcx/clock.c deleted file mode 100644 index ad611973be..0000000000 --- a/chip/npcx/clock.c +++ /dev/null @@ -1,517 +0,0 @@ -/* Copyright 2014 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* Clocks and power management settings */ - -#include "clock.h" -#include "clock_chip.h" -#include "common.h" -#include "console.h" -#include "cpu.h" -#include "gpio.h" -#include "gpio_chip.h" -#include "hooks.h" -#include "hwtimer.h" -#include "hwtimer_chip.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "uart.h" -#include "uartn.h" -#include "util.h" -#include "watchdog.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CLOCK, outstr) -#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args) - -#define WAKE_INTERVAL 61 /* Unit: 61 usec */ -#define IDLE_PARAMS 0x7 /* Support deep idle, instant wake-up */ - -/* Low power idle statistics */ -#ifdef CONFIG_LOW_POWER_IDLE -static int idle_sleep_cnt; -static int idle_dsleep_cnt; -static uint64_t idle_dsleep_time_us; -/* - * Fixed amount of time to keep the console in use flag true after boot in - * order to give a permanent window in which the low speed clock is not used. - */ -#define CONSOLE_IN_USE_ON_BOOT_TIME (15*SECOND) -static int console_in_use_timeout_sec = 15; -static timestamp_t console_expire_time; -#endif - -/** - * Enable clock to peripheral by setting the CGC register pertaining - * to run, sleep, and/or deep sleep modes. - * - * @param offset Offset of the peripheral. See enum clock_gate_offsets. - * @param mask Bit mask of the bits within CGC reg to set. - * @param mode no used - */ -void clock_enable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode) -{ - /* Don't support for different mode */ - uint8_t reg_mask = mask & 0xff; - - /* Set PD bit to 0 */ - NPCX_PWDWN_CTL(offset) &= ~reg_mask; - /* Wait for clock change to take affect. */ - clock_wait_cycles(3); -} - -/** - * Disable clock to peripheral by setting the CGC register pertaining - * to run, sleep, and/or deep sleep modes. - * - * @param offset Offset of the peripheral. See enum clock_gate_offsets. - * @param mask Bit mask of the bits within CGC reg to clear. - * @param mode no used - */ -void clock_disable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode) -{ - /* Don't support for different mode */ - uint8_t reg_mask = mask & 0xff; - - /* Set PD bit to 1 */ - NPCX_PWDWN_CTL(offset) |= reg_mask; - -} - -/*****************************************************************************/ -/* IC specific low-level driver */ - -/** - * Set the CPU clocks and PLLs. - */ -void clock_init(void) -{ -#if defined(CONFIG_CLOCK_SRC_EXTERNAL) && defined(NPCX_EXT32K_OSC_SUPPORT) - /* Select external 32kHz crystal oscillator as LFCLK source. */ - SET_BIT(NPCX_LFCGCTL2, NPCX_LFCGCTL2_XT_OSC_SL_EN); -#endif - - /* - * Resting the OSC_CLK (even to the same value) will make the clock - * unstable for a little which can affect peripheral communication like - * eSPI. Skip this if not needed (e.g. RW jump) - */ - if (NPCX_HFCGN != HFCGN || NPCX_HFCGML != HFCGML - || NPCX_HFCGMH != HFCGMH) { - /* - * Configure frequency multiplier M/N values according to - * the requested OSC_CLK (Unit:Hz). - */ - NPCX_HFCGN = HFCGN; - NPCX_HFCGML = HFCGML; - NPCX_HFCGMH = HFCGMH; - - /* Load M and N values into the frequency multiplier */ - SET_BIT(NPCX_HFCGCTRL, NPCX_HFCGCTRL_LOAD); - /* Wait for stable */ - while (IS_BIT_SET(NPCX_HFCGCTRL, NPCX_HFCGCTRL_CLK_CHNG)) - ; - } - - /* Set all clock prescalers of core and peripherals. */ -#if defined(CHIP_FAMILY_NPCX5) - NPCX_HFCGP = (FPRED << 4); - NPCX_HFCBCD = (NPCX_HFCBCD & 0xF0) | (APB1DIV | (APB2DIV << 2)); -#elif NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX7 - NPCX_HFCGP = ((FPRED << 4) | AHB6DIV); - NPCX_HFCBCD = (FIUDIV << 4); - NPCX_HFCBCD1 = (APB1DIV | (APB2DIV << 4)); -#if NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX9 - NPCX_HFCBCD2 = (APB3DIV | (APB4DIV << 4)); -#else - NPCX_HFCBCD2 = APB3DIV; -#endif -#endif - - /* Notify modules of frequency change */ - hook_notify(HOOK_FREQ_CHANGE); - - /* Configure alt. clock GPIOs (eg. optional 32KHz clock) */ - gpio_config_module(MODULE_CLOCK, 1); -} - -#if defined(CHIP_FAMILY_NPCX5) -void clock_turbo(void) -{ - /* Configure Frequency multiplier values to 50MHz */ - NPCX_HFCGN = 0x02; - NPCX_HFCGML = 0xEC; - NPCX_HFCGMH = 0x0B; - - /* Load M and N values into the frequency multiplier */ - SET_BIT(NPCX_HFCGCTRL, NPCX_HFCGCTRL_LOAD); - - /* Wait for stable */ - while (IS_BIT_SET(NPCX_HFCGCTRL, NPCX_HFCGCTRL_CLK_CHNG)) - ; - - /* Keep Core CLK & FMCLK are the same if Core CLK exceed 33MHz */ - NPCX_HFCGP = 0x00; - - /* - * Let APB2 equals Core CLK/2 if default APB2 clock is divisible - * by 1MHz - */ - NPCX_HFCBCD = NPCX_HFCBCD & 0xF3; -} -#elif NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX7 -void clock_turbo(void) -{ -#if NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX9 - /* For NPCX9: - * Increase CORE_CLK (CPU) as the same as OSC_CLK. Since - * CORE_CLK > 66MHz, we also need to set FIUDIV as 1 but - * can keep AHB6DIV to 0. - */ - NPCX_HFCGP = 0x00; -#else - /* For NPCX7: - * Increase CORE_CLK (CPU) as the same as OSC_CLK. Since - * CORE_CLK > 66MHz, we also need to set AHB6DIV and FIUDIV as 1. - */ - NPCX_HFCGP = 0x01; -#endif - NPCX_HFCBCD = BIT(4); -} - -void clock_normal(void) -{ - /* Set CORE_CLK (CPU), AHB6_CLK and FIU_CLK back to original values. */ - NPCX_HFCGP = ((FPRED << 4) | AHB6DIV); - NPCX_HFCBCD = (FIUDIV << 4); -} - -void clock_enable_module(enum module_id module, int enable) -{ - /* Assume we have a single task using MODULE_FAST_CPU */ - if (module == MODULE_FAST_CPU) { - if (enable) - clock_turbo(); - else - clock_normal(); - } -} - -#endif - -/** - * Return the current clock frequency in Hz. - */ -int clock_get_freq(void) -{ - return CORE_CLK; -} - -/** - * Return the current FMUL clock frequency in Hz. - */ -int clock_get_fm_freq(void) -{ - return FMCLK; -} - -/** - * Return the current APB1 clock frequency in Hz. - */ -int clock_get_apb1_freq(void) -{ - return NPCX_APB_CLOCK(1); -} - -/** - * Return the current APB2 clock frequency in Hz. - */ -int clock_get_apb2_freq(void) -{ - return NPCX_APB_CLOCK(2); -} - -/** - * Return the current APB3 clock frequency in Hz. - */ -#if NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX7 -int clock_get_apb3_freq(void) -{ - return NPCX_APB_CLOCK(3); -} -#endif - -/** - * Wait for a number of clock cycles. - * - * Simple busy waiting for use before clocks/timers are initialized. - * - * @param cycles Number of cycles to wait. - */ -void clock_wait_cycles(uint32_t cycles) -{ - asm volatile("1: subs %0, #1\n" - " bne 1b\n" : "+r"(cycles)); -} - -#ifdef CONFIG_LOW_POWER_IDLE -void clock_refresh_console_in_use(void) -{ - /* Set console in use expire time. */ - console_expire_time = get_time(); - console_expire_time.val += console_in_use_timeout_sec * SECOND; - return; -} - -#if defined(CHIP_FAMILY_NPCX5) -void clock_uart2gpio(void) -{ - /* Is pimux to UART? */ - if (npcx_is_uart()) { - /* Flush tx before enter deep idle */ - uart_tx_flush(); - /* Change pinmux to GPIO and disable UART IRQ */ - task_disable_irq(NPCX_IRQ_UART); - /* Set to GPIO */ - npcx_uart2gpio(); - /* Clear pending wakeup */ - uart_clear_pending_wakeup(); - /* Enable MIWU for GPIO (UARTRX) */ - uart_enable_wakeup(1); - } -} - -void clock_gpio2uart(void) -{ - /* Is Pending bit of GPIO (UARTRX) */ - if (uart_is_wakeup_from_gpio()) { - /* Refresh console in-use timer */ - clock_refresh_console_in_use(); - /* Disable MIWU for GPIO (UARTRX) */ - uart_enable_wakeup(0); - /* Go back CR_SIN */ - npcx_gpio2uart(); - /* Enable uart again */ - task_enable_irq(NPCX_IRQ_UART); - } -} -#endif - -/* Idle task. Executed when no tasks are ready to be scheduled. */ -void __idle(void) -{ - timestamp_t t0, t1; - uint32_t next_evt; - uint32_t next_evt_us; - uint16_t evt_count; - - /* - * Initialize console in use to true and specify the console expire - * time in order to give a fixed window on boot in which the low speed - * clock will not be used in idle. - */ - console_expire_time.val = get_time().val + CONSOLE_IN_USE_ON_BOOT_TIME; - - while (1) { - /* - * Disable interrupts before going to deep sleep in order to - * calculate the appropriate time to wake up. Note: the wfi - * instruction waits until an interrupt is pending, so it - * will still wake up even with interrupts disabled. - */ - interrupt_disable(); - - /* Compute event delay */ - t0 = get_time(); - next_evt = __hw_clock_event_get(); - - /* Do we have enough time before next event to deep sleep. */ - if (DEEP_SLEEP_ALLOWED && - /* - * Our HW timer doesn't tick in deep sleep - we do manual - * adjustment based on sleep duration after wake. Avoid - * the tricky overflow case by waiting out the period just - * before overflow. - */ - next_evt != EVT_MAX_EXPIRED_US && - /* Ensure event hasn't already expired */ - next_evt > t0.le.lo && - /* Ensure we have sufficient time before expiration */ - next_evt - t0.le.lo > WAKE_INTERVAL && - /* Make sure it's over console expired time */ - t0.val > console_expire_time.val) { -#if DEBUG_CLK - /* Use GPIO to indicate SLEEP mode */ - CLEAR_BIT(NPCX_PDOUT(0), 0); -#endif - idle_dsleep_cnt++; - - /* Enable Host access wakeup */ - SET_BIT(NPCX_WKEN(MIWU_TABLE_0, MIWU_GROUP_5), 6); - -#if defined(CHIP_FAMILY_NPCX5) - /* UART-rx(console) become to GPIO (NONE INT mode) */ - clock_uart2gpio(); -#elif NPCX_FAMILY_VERSION >= NPCX_FAMILY_NPCX7 - uartn_wui_en(CONFIG_CONSOLE_UART); -#endif - - /* - * Disable input buffer of all 1.8v i2c ports before - * entering deep sleep for better power consumption. - */ - gpio_enable_1p8v_i2c_wake_up_input(0); - - /* Set deep idle - instant wake-up mode */ - NPCX_PMCSR = IDLE_PARAMS; - - /* Get current counter value of event timer */ - evt_count = __hw_clock_event_count(); - - /* - * TODO (ML): We found the same symptom of idle occurs - * after wake-up from deep idle. Please see task.c for - * more detail. - * Workaround: Apply the same bypass of idle. - */ - asm ("push {r0-r5}\n" - "wfi\n" - "ldm %0, {r0-r5}\n" - "pop {r0-r5}\n" - "isb\n" :: "r" (0x100A8000) - ); - - /* Get time delay cause of deep idle */ - next_evt_us = __hw_clock_get_sleep_time(evt_count); - - /* - * Clear PMCSR manually in case there's wake-up between - * setting it and wfi. - */ - NPCX_PMCSR = 0; -#if defined(CHIP_FAMILY_NPCX5) - /* GPIO back to UART-rx (console) */ - clock_gpio2uart(); -#endif - - /* Enable input buffer of all 1.8v i2c ports. */ - gpio_enable_1p8v_i2c_wake_up_input(1); - - /* Record time spent in deep sleep. */ - idle_dsleep_time_us += next_evt_us; - - /* Fast forward timer according to wake-up timer. */ - t1.val = t0.val + next_evt_us; - /* Leave overflow situation for ITIM32 */ - if (t1.le.hi == t0.le.hi) - force_time(t1); - } else { -#if DEBUG_CLK - /* Use GPIO to indicate NORMAL mode */ - SET_BIT(NPCX_PDOUT(0), 0); -#endif - idle_sleep_cnt++; - - /* - * Using host access to make sure M4 core clock will - * return when the eSPI accesses the Host modules if - * CSAE bit is set. Please notice this symptom only - * occurs at npcx5. - */ -#if defined(CHIP_FAMILY_NPCX5) && defined(CONFIG_HOSTCMD_ESPI) - /* Enable Host access wakeup */ - SET_BIT(NPCX_WKEN(MIWU_TABLE_0, MIWU_GROUP_5), 6); -#endif - /* - * Normal idle : wait for interrupt - * TODO (ML): Workaround method for wfi issue. - * Please see task.c for more detail - */ - asm ("push {r0-r5}\n" - "wfi\n" - "ldm %0, {r0-r5}\n" - "pop {r0-r5}\n" - "isb\n" :: "r" (0x100A8000) - ); - } - - /* - * Restore interrupt - * RTOS will leave idle task to handle ISR which wakes up EC - */ - interrupt_enable(); - } -} -#endif /* CONFIG_LOW_POWER_IDLE */ - - -#ifdef CONFIG_LOW_POWER_IDLE -/** - * Print low power idle statistics - */ -static int command_idle_stats(int argc, char **argv) -{ - timestamp_t ts = get_time(); - - ccprintf("Num idle calls that sleep: %d\n", idle_sleep_cnt); - ccprintf("Num idle calls that deep-sleep: %d\n", idle_dsleep_cnt); - ccprintf("Time spent in deep-sleep: %.6llds\n", - idle_dsleep_time_us); - ccprintf("Total time on: %.6llds\n", ts.val); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(idlestats, command_idle_stats, - "", - "Print last idle stats"); - -/** - * Configure deep sleep clock settings. - */ -static int command_dsleep(int argc, char **argv) -{ - int v; - - if (argc > 1) { - if (parse_bool(argv[1], &v)) { - /* - * Force deep sleep not to use low speed clock or - * allow it to use the low speed clock. - */ - if (v) - disable_sleep(SLEEP_MASK_FORCE_NO_LOW_SPEED); - else - enable_sleep(SLEEP_MASK_FORCE_NO_LOW_SPEED); - } else { - /* Set console in use timeout. */ - char *e; - v = strtoi(argv[1], &e, 10); - if (*e) - return EC_ERROR_PARAM1; - - console_in_use_timeout_sec = v; - - /* Refresh console in use to use new timeout. */ - clock_refresh_console_in_use(); - } - } - - ccprintf("Sleep mask: %08x\n", sleep_mask); - ccprintf("Console in use timeout: %d sec\n", - console_in_use_timeout_sec); - ccprintf("PMCSR register: 0x%02x\n", NPCX_PMCSR); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(dsleep, command_dsleep, - "[ on | off | <timeout> sec]", - "Deep sleep clock settings:\nUse 'on' to force deep " - "sleep not to use low speed clock.\nUse 'off' to " - "allow deep sleep to auto-select using the low speed " - "clock.\n" - "Give a timeout value for the console in use timeout.\n" - "See also 'sleepmask'."); -#endif /* CONFIG_LOW_POWER_IDLE */ |