| /* Copyright (c) 2012 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 "common.h" |
| #include "console.h" |
| #include "cpu.h" |
| #include "gpio.h" |
| #include "hooks.h" |
| #include "hwtimer.h" |
| #include "registers.h" |
| #include "system.h" |
| #include "task.h" |
| #include "timer.h" |
| #include "uart.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 PLL_CLOCK 66666667 /* System clock = 200MHz PLL/3 = 66.667MHz */ |
| |
| #ifdef CONFIG_LOW_POWER_USE_LFIOSC |
| /* |
| * Length of time for the processor to wake up from deep sleep. Actual |
| * measurement gives anywhere up to 780us, depending on the mode it is coming |
| * out of. The datasheet gives a maximum of 846us, for coming out of deep |
| * sleep in our worst case deep sleep mode. |
| */ |
| #define DEEP_SLEEP_RECOVER_TIME_USEC 850 |
| #else |
| /* |
| * Length of time for the processor to wake up from deep sleep. Datasheet |
| * maximum is 145us, but in practice have seen as much as 336us. |
| */ |
| #define DEEP_SLEEP_RECOVER_TIME_USEC 400 |
| #endif |
| |
| /* 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; |
| static int dsleep_recovery_margin_us = 1000000; |
| |
| /* |
| * 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 = 60; |
| static timestamp_t console_expire_time; |
| #endif |
| |
| static int freq; |
| |
| /** |
| * Disable the PLL; run off internal oscillator. |
| */ |
| static void disable_pll(void) |
| { |
| /* Switch to 16MHz internal oscillator and power down the PLL */ |
| LM4_SYSTEM_RCC = LM4_SYSTEM_RCC_SYSDIV(0) | |
| LM4_SYSTEM_RCC_BYPASS | |
| LM4_SYSTEM_RCC_PWRDN | |
| LM4_SYSTEM_RCC_OSCSRC(1) | |
| LM4_SYSTEM_RCC_MOSCDIS; |
| |
| #ifdef CONFIG_LOW_POWER_IDLE |
| /* |
| * If using the low power idle, then set the ACG bit, which specifies |
| * that the sleep and deep sleep modes are using their own clock gating |
| * registers SCGC and DCGS respectively instead of using the run mode |
| * clock gating registers RCGC. |
| */ |
| LM4_SYSTEM_RCC |= LM4_SYSTEM_RCC_ACG; |
| #endif |
| |
| LM4_SYSTEM_RCC2 &= ~LM4_SYSTEM_RCC2_USERCC2; |
| |
| freq = INTERNAL_CLOCK; |
| } |
| |
| /** |
| * Enable the PLL to run at full clock speed. |
| */ |
| static void enable_pll(void) |
| { |
| /* Disable the PLL so we can reconfigure it */ |
| disable_pll(); |
| |
| /* |
| * Enable the PLL (PWRDN is no longer set) and set divider. PLL is |
| * still bypassed, since it hasn't locked yet. |
| */ |
| LM4_SYSTEM_RCC = LM4_SYSTEM_RCC_SYSDIV(2) | |
| LM4_SYSTEM_RCC_USESYSDIV | |
| LM4_SYSTEM_RCC_BYPASS | |
| LM4_SYSTEM_RCC_OSCSRC(1) | |
| LM4_SYSTEM_RCC_MOSCDIS; |
| |
| #ifdef CONFIG_LOW_POWER_IDLE |
| /* |
| * If using the low power idle, then set the ACG bit, which specifies |
| * that the sleep and deep sleep modes are using their own clock gating |
| * registers SCGC and DCGS respectively instead of using the run mode |
| * clock gating registers RCGC. |
| */ |
| LM4_SYSTEM_RCC |= LM4_SYSTEM_RCC_ACG; |
| #endif |
| |
| /* Wait for the PLL to lock */ |
| clock_wait_cycles(1024); |
| while (!(LM4_SYSTEM_PLLSTAT & 1)) |
| ; |
| |
| /* Remove bypass on PLL */ |
| LM4_SYSTEM_RCC &= ~LM4_SYSTEM_RCC_BYPASS; |
| freq = PLL_CLOCK; |
| } |
| |
| void clock_enable_pll(int enable, int notify) |
| { |
| if (enable) |
| enable_pll(); |
| else |
| disable_pll(); |
| |
| /* Notify modules of frequency change */ |
| if (notify) |
| hook_notify(HOOK_FREQ_CHANGE); |
| } |
| |
| void clock_wait_cycles(uint32_t cycles) |
| { |
| asm("1: subs %0, #1\n" |
| " bne 1b\n" :: "r"(cycles)); |
| } |
| |
| int clock_get_freq(void) |
| { |
| return freq; |
| } |
| |
| void clock_init(void) |
| { |
| #ifdef BOARD_BDS |
| /* |
| * Perform an auto calibration of the internal oscillator using the |
| * 32.768KHz hibernate clock, unless we've already done so. This is |
| * only necessary on A2 silicon as on BDS; A3 silicon is all |
| * factory-trimmed. |
| */ |
| if ((LM4_SYSTEM_PIOSCSTAT & 0x300) != 0x100) { |
| /* Start calibration */ |
| LM4_SYSTEM_PIOSCCAL = 0x80000000; |
| LM4_SYSTEM_PIOSCCAL = 0x80000200; |
| /* Wait for result */ |
| clock_wait_cycles(16); |
| while (!(LM4_SYSTEM_PIOSCSTAT & 0x300)) |
| ; |
| } |
| #else |
| /* |
| * Only BDS has an external crystal; other boards don't have one, and |
| * can disable main oscillator control to reduce power consumption. |
| */ |
| LM4_SYSTEM_MOSCCTL = 0x04; |
| #endif |
| |
| /* Make sure PLL is disabled */ |
| disable_pll(); |
| } |
| |
| void clock_enable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode) |
| { |
| if (mode & CGC_MODE_RUN) |
| *(LM4_SYSTEM_RCGC_BASE + offset) |= mask; |
| |
| if (mode & CGC_MODE_SLEEP) |
| *(LM4_SYSTEM_SCGC_BASE + offset) |= mask; |
| |
| if (mode & CGC_MODE_DSLEEP) |
| *(LM4_SYSTEM_DCGC_BASE + offset) |= mask; |
| |
| /* Wait for clock change to take affect. */ |
| clock_wait_cycles(3); |
| } |
| |
| void clock_disable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode) |
| { |
| if (mode & CGC_MODE_RUN) |
| *(LM4_SYSTEM_RCGC_BASE + offset) &= ~mask; |
| |
| if (mode & CGC_MODE_SLEEP) |
| *(LM4_SYSTEM_SCGC_BASE + offset) &= ~mask; |
| |
| if (mode & CGC_MODE_DSLEEP) |
| *(LM4_SYSTEM_DCGC_BASE + offset) &= ~mask; |
| } |
| |
| /* |
| * The low power idle task does not support using the EEPROM, |
| * because it is dangerous to go to deep sleep while EEPROM |
| * transaction is in progress. To fix, LM4_EEPROM_EEDONE, should |
| * be checked before going in to deep sleep. |
| */ |
| #if defined(CONFIG_LOW_POWER_IDLE) && defined(CONFIG_EEPROM) |
| #error "Low power idle mode does not support use of EEPROM" |
| #endif |
| |
| #ifdef CONFIG_LOW_POWER_IDLE |
| |
| void clock_refresh_console_in_use(void) |
| { |
| disable_sleep(SLEEP_MASK_CONSOLE); |
| |
| /* Set console in use expire time. */ |
| console_expire_time = get_time(); |
| console_expire_time.val += console_in_use_timeout_sec * SECOND; |
| |
| } |
| |
| /* Low power idle task. Executed when no tasks are ready to be scheduled. */ |
| void __idle(void) |
| { |
| timestamp_t t0, t1, rtc_t0, rtc_t1; |
| int next_delay = 0; |
| int time_for_dsleep, margin_us; |
| int use_low_speed_clock; |
| |
| /* Enable the hibernate IRQ used to wake up from deep sleep */ |
| system_enable_hib_interrupt(); |
| |
| /* Set SRAM and flash power management to 'low power' in deep sleep. */ |
| LM4_SYSTEM_DSLPPWRCFG = 0x23; |
| |
| /* Enable JTAG interrupt which will notify us when JTAG is in use. */ |
| gpio_enable_interrupt(GPIO_JTAG_TCK); |
| |
| /* |
| * 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. |
| */ |
| disable_sleep(SLEEP_MASK_CONSOLE); |
| console_expire_time.val = get_time().val + CONSOLE_IN_USE_ON_BOOT_TIME; |
| |
| /* |
| * Print when the idle task starts. This is the lowest priority task, |
| * so this only starts once all other tasks have gotten a chance to do |
| * their task inits and have gone to sleep. |
| */ |
| CPRINTS("low power idle task started"); |
| |
| 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(); |
| |
| t0 = get_time(); |
| next_delay = __hw_clock_event_get() - t0.le.lo; |
| |
| /* Do we have enough time before next event to deep sleep. */ |
| time_for_dsleep = next_delay > (DEEP_SLEEP_RECOVER_TIME_USEC + |
| HIB_SET_RTC_MATCH_DELAY_USEC); |
| |
| if (DEEP_SLEEP_ALLOWED && time_for_dsleep) { |
| /* Deep-sleep in STOP mode. */ |
| idle_dsleep_cnt++; |
| |
| /* Check if the console use has expired. */ |
| if ((sleep_mask & SLEEP_MASK_CONSOLE) && |
| t0.val > console_expire_time.val) { |
| /* Enable low speed deep sleep. */ |
| enable_sleep(SLEEP_MASK_CONSOLE); |
| |
| /* |
| * Wait one clock before checking if low speed |
| * deep sleep is allowed to give time for |
| * sleep mask to update. |
| */ |
| clock_wait_cycles(1); |
| |
| if (LOW_SPEED_DEEP_SLEEP_ALLOWED) |
| CPRINTS("Disabling console in " |
| "deep sleep"); |
| } |
| |
| /* |
| * Determine if we should use a lower clock speed or |
| * keep the same (16MHz) clock in deep sleep. Use the |
| * lower speed only if the sleep mask specifies that low |
| * speed sleep is allowed, the console UART TX is not |
| * busy, and the console UART buffer is empty. |
| */ |
| use_low_speed_clock = LOW_SPEED_DEEP_SLEEP_ALLOWED && |
| !uart_tx_in_progress() && uart_buffer_empty(); |
| |
| #ifdef CONFIG_LOW_POWER_USE_LFIOSC |
| /* Set the deep sleep clock register. Use either the |
| * normal PIOSC (16MHz) or the LFIOSC (32kHz). */ |
| LM4_SYSTEM_DSLPCLKCFG = use_low_speed_clock ? |
| 0x32 : 0x10; |
| #else |
| /* |
| * Set the deep sleep clock register. Use either the |
| * PIOSC with no divider (16MHz) or the PIOSC with |
| * a /64 divider (250kHz). |
| */ |
| LM4_SYSTEM_DSLPCLKCFG = use_low_speed_clock ? |
| 0x1f800010 : 0x10; |
| #endif |
| |
| /* |
| * If using low speed clock, disable console. |
| * This will also convert the console RX pin to a GPIO |
| * and set an edge interrupt to wake us from deep sleep |
| * if any action occurs on console. |
| */ |
| if (use_low_speed_clock) |
| uart_enter_dsleep(); |
| |
| /* Set deep sleep bit. */ |
| CPU_SCB_SYSCTRL |= 0x4; |
| |
| /* Record real time before sleeping. */ |
| rtc_t0 = system_get_rtc(); |
| |
| /* |
| * Set RTC interrupt in time to wake up before |
| * next event. |
| */ |
| system_set_rtc_alarm(0, next_delay - |
| DEEP_SLEEP_RECOVER_TIME_USEC); |
| |
| /* Wait for interrupt: goes into deep sleep. */ |
| asm("wfi"); |
| |
| /* Clear deep sleep bit. */ |
| CPU_SCB_SYSCTRL &= ~0x4; |
| |
| /* Disable and clear RTC interrupt. */ |
| system_reset_rtc_alarm(); |
| |
| /* Fast forward timer according to RTC counter. */ |
| rtc_t1 = system_get_rtc(); |
| t1.val = t0.val + (rtc_t1.val - rtc_t0.val); |
| force_time(t1); |
| |
| /* If using low speed clock, re-enable the console. */ |
| if (use_low_speed_clock) |
| uart_exit_dsleep(); |
| |
| /* Record time spent in deep sleep. */ |
| idle_dsleep_time_us += (rtc_t1.val - rtc_t0.val); |
| |
| /* Calculate how close we were to missing deadline */ |
| margin_us = next_delay - (int)(rtc_t1.val - rtc_t0.val); |
| if (margin_us < 0) |
| CPRINTS("overslept by %dus", -margin_us); |
| |
| /* Record the closest to missing a deadline. */ |
| if (margin_us < dsleep_recovery_margin_us) |
| dsleep_recovery_margin_us = margin_us; |
| } else { |
| idle_sleep_cnt++; |
| |
| /* Normal idle : only CPU clock stopped. */ |
| asm("wfi"); |
| } |
| interrupt_enable(); |
| } |
| } |
| #endif /* CONFIG_LOW_POWER_IDLE */ |
| |
| /*****************************************************************************/ |
| /* Console commands */ |
| |
| #ifdef CONFIG_CMD_SLEEP |
| /** |
| * Measure baseline for power consumption. |
| * |
| * Levels : |
| * 0 : CPU running in tight loop |
| * 1 : CPU running in tight loop but peripherals gated |
| * 2 : CPU in sleep mode |
| * 3 : CPU in sleep mode and peripherals gated |
| * 4 : CPU in deep sleep mode |
| * 5 : CPU in deep sleep mode and peripherals gated |
| * |
| * Clocks : |
| * 0 : No change |
| * 1 : 16MHz |
| * 2 : 1 MHz |
| * 3 : 30kHz |
| * |
| * SRAM Power Management: |
| * 0 : Active |
| * 1 : Standby |
| * 3 : Low Power |
| * |
| * Flash Power Management: |
| * 0 : Active |
| * 2 : Low Power |
| */ |
| static int command_sleep(int argc, char **argv) |
| { |
| int level = 0; |
| int clock = 0; |
| int sram_pm = 0; |
| int flash_pm = 0; |
| uint32_t uartibrd = 0; |
| uint32_t uartfbrd = 0; |
| |
| if (argc >= 2) |
| level = strtoi(argv[1], NULL, 10); |
| if (argc >= 3) |
| clock = strtoi(argv[2], NULL, 10); |
| if (argc >= 4) |
| sram_pm = strtoi(argv[3], NULL, 10); |
| if (argc >= 5) |
| flash_pm = strtoi(argv[4], NULL, 10); |
| |
| #ifdef BOARD_BDS |
| /* Remove LED current sink. */ |
| gpio_set_level(GPIO_DEBUG_LED, 0); |
| #endif |
| |
| ccprintf("Sleep : level %d, clock %d, sram pm %d, flash_pm %d...\n", |
| level, clock, sram_pm, flash_pm); |
| cflush(); |
| |
| /* Set clock speed. */ |
| if (clock) { |
| /* Use ROM code function to set the clock */ |
| void **func_table = (void **)*(uint32_t *)0x01000044; |
| void (*rom_clock_set)(uint32_t rcc) = func_table[23]; |
| |
| /* Disable interrupts. */ |
| asm volatile("cpsid i"); |
| |
| switch (clock) { |
| case 1: /* 16MHz IOSC */ |
| uartibrd = 17; |
| uartfbrd = 23; |
| rom_clock_set(0x00000d51); |
| break; |
| case 2: /* 1MHz IOSC */ |
| uartibrd = 1; |
| uartfbrd = 5; |
| rom_clock_set(0x07C00d51); |
| break; |
| case 3: /* 30 kHz */ |
| uartibrd = 0; |
| uartfbrd = 0; |
| rom_clock_set(0x00000d71); |
| break; |
| } |
| |
| /* |
| * TODO(crosbug.com/p/23795): move this to the UART module; |
| * ugly to have UARTisms here. Also note this only fixes |
| * UART0, not UART1. Should just be able to trigger |
| * HOOK_FREQ_CHANGE and have that take care of it. |
| */ |
| if (uartfbrd) { |
| /* Disable the port via UARTCTL and add HSE. */ |
| LM4_UART_CTL(0) = 0x0320; |
| /* Set the baud rate divisor. */ |
| LM4_UART_IBRD(0) = uartibrd; |
| LM4_UART_FBRD(0) = uartfbrd; |
| /* Poke UARTLCRH to make the new divisor take effect. */ |
| LM4_UART_LCRH(0) = LM4_UART_LCRH(0); |
| /* Enable the port. */ |
| LM4_UART_CTL(0) |= 0x0001; |
| } |
| asm volatile("cpsie i"); |
| } |
| |
| if (uartfbrd) { |
| ccprintf("We are still alive. RCC=%08x\n", LM4_SYSTEM_RCC); |
| cflush(); |
| } |
| |
| /* Enable interrupts. */ |
| asm volatile("cpsid i"); |
| |
| /* gate peripheral clocks */ |
| if (level & 1) { |
| clock_disable_peripheral(CGC_OFFSET_WD, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_TIMER, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_GPIO, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_DMA, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_HIB, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_UART, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_SSI, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_I2C, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_ADC, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_LPC, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_PECI, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_FAN, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_EEPROM, 0xffffffff, |
| CGC_MODE_ALL); |
| clock_disable_peripheral(CGC_OFFSET_WTIMER, 0xffffffff, |
| CGC_MODE_ALL); |
| } |
| |
| /* Set deep sleep bit. */ |
| if (level >= 4) |
| CPU_SCB_SYSCTRL |= 0x4; |
| |
| /* Set SRAM and flash PM for sleep and deep sleep. */ |
| LM4_SYSTEM_SLPPWRCFG = (flash_pm << 4) | sram_pm; |
| LM4_SYSTEM_DSLPPWRCFG = (flash_pm << 4) | sram_pm; |
| |
| /* Go to low power mode (forever ...) */ |
| if (level > 1) |
| while (1) { |
| asm("wfi"); |
| watchdog_reload(); |
| } |
| else |
| while (1) |
| watchdog_reload(); |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(sleep, command_sleep, |
| "[level [clock] [sram pm] [flash pm]]", |
| "Drop into sleep", |
| NULL); |
| #endif /* CONFIG_CMD_SLEEP */ |
| |
| #ifdef CONFIG_CMD_PLL |
| |
| static int command_pll(int argc, char **argv) |
| { |
| int v; |
| |
| /* Toggle the PLL */ |
| if (argc > 1) { |
| if (parse_bool(argv[1], &v)) { |
| clock_enable_pll(v, 1); |
| } else { |
| /* Disable PLL and set extra divider */ |
| char *e; |
| v = strtoi(argv[1], &e, 10); |
| if (*e) |
| return EC_ERROR_PARAM1; |
| |
| LM4_SYSTEM_RCC = LM4_SYSTEM_RCC_SYSDIV(v - 1) | |
| LM4_SYSTEM_RCC_BYPASS | |
| LM4_SYSTEM_RCC_PWRDN | |
| LM4_SYSTEM_RCC_OSCSRC(1) | |
| LM4_SYSTEM_RCC_MOSCDIS; |
| |
| freq = INTERNAL_CLOCK / v; |
| |
| /* Notify modules of frequency change */ |
| hook_notify(HOOK_FREQ_CHANGE); |
| } |
| } |
| |
| /* Print current PLL state */ |
| ccprintf("RCC: 0x%08x\n", LM4_SYSTEM_RCC); |
| ccprintf("RCC2: 0x%08x\n", LM4_SYSTEM_RCC2); |
| ccprintf("PLLSTAT: 0x%08x\n", LM4_SYSTEM_PLLSTAT); |
| ccprintf("Clock: %d Hz\n", clock_get_freq()); |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(pll, command_pll, |
| "[ on | off | <div> ]", |
| "Get/set PLL state", |
| NULL); |
| |
| #endif /* CONFIG_CMD_PLL */ |
| |
| #ifdef CONFIG_CMD_CLOCKGATES |
| /** |
| * Print all clock gating registers |
| */ |
| static int command_clock_gating(int argc, char **argv) |
| { |
| ccprintf(" Run , Sleep , Deep Sleep\n"); |
| |
| ccprintf("WD: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_WD)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_WD)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_WD)); |
| |
| ccprintf("TIMER: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_TIMER)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_TIMER)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_TIMER)); |
| |
| ccprintf("GPIO: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_GPIO)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_GPIO)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_GPIO)); |
| |
| ccprintf("DMA: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_DMA)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_DMA)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_DMA)); |
| |
| ccprintf("HIB: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_HIB)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_HIB)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_HIB)); |
| |
| ccprintf("UART: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_UART)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_UART)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_UART)); |
| |
| ccprintf("SSI: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_SSI)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_SSI)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_SSI)); |
| |
| ccprintf("I2C: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_I2C)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_I2C)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_I2C)); |
| |
| ccprintf("ADC: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_ADC)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_ADC)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_ADC)); |
| |
| ccprintf("LPC: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_LPC)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_LPC)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_LPC)); |
| |
| ccprintf("PECI: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_PECI)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_PECI)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_PECI)); |
| |
| ccprintf("FAN: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_FAN)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_FAN)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_FAN)); |
| |
| ccprintf("EEPROM: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_EEPROM)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_EEPROM)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_EEPROM)); |
| |
| ccprintf("WTIMER: 0x%08x, ", |
| *(LM4_SYSTEM_RCGC_BASE + CGC_OFFSET_WTIMER)); |
| ccprintf("0x%08x, ", *(LM4_SYSTEM_SCGC_BASE + CGC_OFFSET_WTIMER)); |
| ccprintf("0x%08x\n", *(LM4_SYSTEM_DCGC_BASE + CGC_OFFSET_WTIMER)); |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(clockgates, command_clock_gating, |
| "", |
| "Get state of the clock gating controls regs", |
| NULL); |
| #endif /* CONFIG_CMD_CLOCKGATES */ |
| |
| #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: %.6lds\n", |
| idle_dsleep_time_us); |
| ccprintf("Total time on: %.6lds\n", ts.val); |
| ccprintf("Deep-sleep closest to wake deadline: %dus\n", |
| dsleep_recovery_margin_us); |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(idlestats, command_idle_stats, |
| "", |
| "Print last idle stats", |
| NULL); |
| |
| /** |
| * 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("DSLPCLKCFG register: 0x%08x\n", LM4_SYSTEM_DSLPCLKCFG); |
| |
| 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'.", |
| NULL); |
| #endif /* CONFIG_LOW_POWER_IDLE */ |
| |