mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-05 06:21:53 +00:00
The IOUT pin of the smart battery charger can be used to monitor the AC
adapter current (default) or the battery charging current.
BUG=none
BRANCH=none
TEST=manual
Discharge the battery a bit, and connect to the EC console. With the AC
power plugged in, the "battery" command should show charging status,
including current.
The "adc" command will display the A-D converters, including the current
measurement. For example:
> battery
Temp: 0x0b88 = 295.2 K (22.1 C)
Manuf: SMP-COS20
Device: OC2
Chem: LION
Serial: 0x0005
V: 0x4130 = 16688 mV
V-desired: 0x41a0 = 16800 mV
V-design: 0x39d0 = 14800 mV
I: 0x008e = 142 mA(CHG)
I-desired: 0x0080 = 128 mA
Mode: 0x6001
Charge: 98 %
Abs: 94 %
Remaining: 1871 mAh
Cap-full: 1923 mAh
Design: 2000 mAh
Time-full: 0h:23
Empty: 0h:0
>
> adc
ADC channel "ECTemp" = 317
ADC channel "ChargerCurrent" = 455
>
That current is significantly higher than the "I:" reported by the "battery"
command. But look at the charger options:
> sbc 0x12
0x7904 (30980)
>
Bit 5 controls the IOUT Selection. When clear, it monitors the current from
the AC adapter. Set bit 5 to monitor the current provided to the battery:
> sbc 0x12 0x7924
> adc
ADC channel "ECTemp" = 318
ADC channel "ChargerCurrent" = 128
>
That matches what the smart battery sees.
Change-Id: I2fe351304421dfb22d83ef13d416aa44c9f56e8a
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/57940
Reviewed-by: Randall Spangler <rspangler@chromium.org>
292 lines
6.5 KiB
C
292 lines
6.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 "clock.h"
|
|
#include "common.h"
|
|
#include "console.h"
|
|
#include "cpu.h"
|
|
#include "gpio.h"
|
|
#include "hooks.h"
|
|
#include "registers.h"
|
|
#include "system.h"
|
|
#include "task.h"
|
|
#include "timer.h"
|
|
#include "util.h"
|
|
#include "watchdog.h"
|
|
|
|
#define PLL_CLOCK 66666667 /* System clock = 200MHz PLL/3 = 66.667MHz */
|
|
|
|
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;
|
|
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;
|
|
|
|
/* 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
|
|
|
|
/*
|
|
* TODO: UART seems to glitch unless we wait 500k cycles before
|
|
* enabling the PLL, but only if this is a cold boot. Why? UART
|
|
* doesn't even use the PLL'd system clock. I've heard rumors the
|
|
* Stellaris ROM library does this too, but why?
|
|
*/
|
|
if (!system_jumped_to_this_image())
|
|
clock_wait_cycles(500000);
|
|
|
|
/* Make sure PLL is disabled */
|
|
disable_pll();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* 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
|
|
*/
|
|
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
|
|
|
|
ccprintf("Going to sleep : level %d clock %d...\n", level, clock);
|
|
cflush();
|
|
|
|
/* 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) {
|
|
ccprintf("We are still alive. RCC=%08x\n", LM4_SYSTEM_RCC);
|
|
cflush();
|
|
}
|
|
|
|
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)
|
|
CPU_SCB_SYSCTRL |= 0x4;
|
|
/* 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]]",
|
|
"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 */
|