mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-08 16:41:55 +00:00
Instead of using a runtime callback to register the console commands, put them in a special linker section. So we can do a macro to "register" them during the build. It saves 684 bytes and a few microseconds at startup. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BUG=None TEST=run a few commands from the BDS command line. Change-Id: Id33ea210b9035bf76ed720373c74c5dd24ccd1b1
199 lines
4.5 KiB
C
199 lines
4.5 KiB
C
/* 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 <stdint.h>
|
|
|
|
#include "board.h"
|
|
#include "clock.h"
|
|
#include "config.h"
|
|
#include "console.h"
|
|
#include "gpio.h"
|
|
#include "task.h"
|
|
#include "timer.h"
|
|
#include "uart.h"
|
|
#include "registers.h"
|
|
#include "util.h"
|
|
|
|
/**
|
|
* Idle task
|
|
* executed when no task are ready to be scheduled
|
|
*/
|
|
void __idle(void)
|
|
{
|
|
while (1) {
|
|
/* wait for the irq event */
|
|
asm("wfi");
|
|
/* TODO more power management here */
|
|
}
|
|
}
|
|
|
|
/* simple busy waiting before clocks are initialized */
|
|
static void wait_cycles(uint32_t cycles)
|
|
{
|
|
asm("1: subs %0, #1\n"
|
|
" bne 1b\n" :: "r"(cycles));
|
|
}
|
|
|
|
/**
|
|
* Function to 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
|
|
*/
|
|
static int command_sleep(int argc, char **argv)
|
|
{
|
|
int level = 0;
|
|
int clock = 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);
|
|
}
|
|
|
|
#ifdef BOARD_bds
|
|
/* remove LED current sink */
|
|
gpio_set_level(GPIO_DEBUG_LED, 0);
|
|
#endif
|
|
|
|
uart_printf("Going to sleep : level %d clock %d...\n", level, clock);
|
|
uart_flush_output();
|
|
|
|
/* clock setting */
|
|
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: move this to the UART module; ugly to have
|
|
UARTisms here. Also note this only fixes UART0,
|
|
not UART1. */
|
|
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) {
|
|
uart_printf("We are still alive. RCC=%08x\n", LM4_SYSTEM_RCC);
|
|
uart_flush_output();
|
|
}
|
|
|
|
asm volatile("cpsid i");
|
|
|
|
/* gate peripheral clocks */
|
|
if (level & 1) {
|
|
LM4_SYSTEM_RCGCTIMER = 0;
|
|
LM4_SYSTEM_RCGCGPIO = 0;
|
|
LM4_SYSTEM_RCGCDMA = 0;
|
|
LM4_SYSTEM_RCGCUART = 0;
|
|
LM4_SYSTEM_RCGCLPC = 0;
|
|
LM4_SYSTEM_RCGCWTIMER = 0;
|
|
}
|
|
/* set deep sleep bit */
|
|
if (level >= 4)
|
|
LM4_SCB_SYSCTRL |= 0x4;
|
|
/* go to low power mode (forever ...) */
|
|
if (level > 1)
|
|
while (1)
|
|
asm("wfi");
|
|
else
|
|
while(1);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(sleep, command_sleep);
|
|
|
|
|
|
static void clock_init_pll(uint32_t value)
|
|
{
|
|
/**
|
|
* at startup, OSCSRC is PIOSC (precision internal oscillator)
|
|
* PLL and PLL2 are in power-down
|
|
*/
|
|
|
|
/* PLL already setup */
|
|
if (LM4_SYSTEM_PLLSTAT & 1)
|
|
return;
|
|
|
|
/* Put a bypass on the system clock PLLs, no divider */
|
|
LM4_SYSTEM_RCC = (LM4_SYSTEM_RCC | 0x800) & ~0x400000;
|
|
LM4_SYSTEM_RCC2 = (LM4_SYSTEM_RCC2 | 0x800) & ~0x80000000;
|
|
|
|
/* Enable main and precision internal oscillators */
|
|
LM4_SYSTEM_RCC &= ~0x3;
|
|
|
|
/* Perform an auto calibration of the internal oscillator, using the
|
|
* 32.768KHz hibernate clock. */
|
|
/* TODO: (crosbug.com/p/7693) This is only needed on early chips which
|
|
* aren't factory trimmed. */
|
|
LM4_SYSTEM_PIOSCCAL = 0x80000000;
|
|
LM4_SYSTEM_PIOSCCAL = 0x80000200;
|
|
|
|
/* wait 1 million CPU cycles */
|
|
wait_cycles(512 * 1024);
|
|
|
|
/* clear PLL lock flag (aka PLLLMIS) */
|
|
LM4_SYSTEM_MISC = 0x40;
|
|
/* clear powerdown / set XTAL frequency, divider, and source */
|
|
LM4_SYSTEM_RCC = (LM4_SYSTEM_RCC & ~0x07c027f0) | (value & 0x07c007f0);
|
|
/* wait 32 CPU cycles */
|
|
wait_cycles(16);
|
|
/* wait for PLL to lock */
|
|
while (!(LM4_SYSTEM_RIS & 0x40));
|
|
|
|
/* Remove bypass on PLL */
|
|
LM4_SYSTEM_RCC = LM4_SYSTEM_RCC & ~0x800;
|
|
}
|
|
|
|
int clock_init(void)
|
|
{
|
|
/* CPU clock = PLL/3 = 66.667MHz; System clock = PLL */
|
|
BUILD_ASSERT(CPU_CLOCK == 66666667);
|
|
/* Osc source = internal 16MHz oscillator */
|
|
clock_init_pll(0x01400550);
|
|
|
|
return EC_SUCCESS;
|
|
}
|